\if

  • Creatore
    Topic
  • #80143
    Up
    0
    Down
    ::


    Ciao a tutti,

    sto cercando di inserire un condizionale in un nuovo comando che sto cercando di scrivere. All’interno del comando vengono calcolate due distanze, con il comando [tt]\calcLV[/tt] indicato in questa discussione. Dopo ciò viene calcolata la loro differenza:
    `\pgfmathsetmacro{\differenza}{\distA-\distB}`
    A questo punto vorrei inserire un condizionale, il quale, se [tt]\differenza[/tt] è nullo deve eseguire un certo codice, altrimenti un altro (ovviamente). Ho provato così:
    `\if \differenza = 0

    \else

    \fi`
    ma non funziona. Non mi vengono indicati errori in fase di compilazione, ma semplicemente non ottengo il comportamento corretto di [tt]\if.[/tt] Cosa sbaglio?

    Grazie e buona giornata,
    Marco.

Visualizzazione 9 filoni di risposte
  • Autore
    Risposte
    • #80144
      Up
      0
      Down
      ::

      Marco87″ post=79330Ciao a tutti,

      sto cercando di inserire un condizionale in un nuovo comando che sto cercando di scrivere. All’interno del comando vengono calcolate due distanze, con il comando [tt]\calcLV[/tt] indicato in questa discussione. Dopo ciò viene calcolata la loro differenza:
      `\pgfmathsetmacro{\differenza}{\distA-\distB}`
      A questo punto vorrei inserire un condizionale, il quale, se [tt]\differenza[/tt] è nullo deve eseguire un certo codice, altrimenti un altro (ovviamente). Ho provato così:
      `\if \differenza = 0

      \else

      \fi`
      ma non funziona. Non mi vengono indicati errori in fase di compilazione, ma semplicemente non ottengo il comportamento corretto di [tt]\if.[/tt] Cosa sbaglio?

      Grazie e buona giornata,
      Marco.

      Immagino che [tt]\differenza[/tt] contenga una dimensione in punti; in tal caso il condizionale da usare è [tt]\ifdim[/tt]:

      `\ifdim\differenza = 0pt

      \else

      \fi`

      Il condizionale [tt]\if[/tt] serve per confrontare due token in base al loro “character code”.

      Ciao
      Enrico

    • #80145
      Up
      0
      Down
      ::


      Ciao Enrico,

      egreg9Immagino che [tt]\differenza[/tt] contenga una dimensione in punti;

      A dire il vero non ne sono sicuro. Il comando [tt]\calcLV[/tt] mostrato nella discussione di cui ho fornito il link nel mio messaggio precedente, determina la distanza fra due punti. Il risultato è fornito in pt, ma non so se si porti dietro anche l’unità di misura; in merito sono parecchio ignorante. 🙁

      Se scrivo:
      `\ifdim\differenza = 0pt

      \else

      \fi`
      mi viene dato il seguente errore:

      Illegal unit of measure (pt inserted). =

      Se invece scrivo:
      `\ifdim\differenza pt = 0pt

      \else

      \fi`
      l’errore scompare, ma il comportamento del codice mostra che quell’uguaglianza non viene verificata correttamente.

      Grazie e buona serata,
      Marco.

    • #80146
      Up
      0
      Down
      ::

      Marco87″ post=79337Ciao Enrico,

      Immagino che [tt]\differenza[/tt] contenga una dimensione in punti;

      A dire il vero non ne sono sicuro. Il comando [tt]\calcLV[/tt] mostrato nella discussione di cui ho fornito il link nel mio messaggio precedente, determina la distanza fra due punti. Il risultato è fornito in pt, ma non so se si porti dietro anche l’unità di misura; in merito sono parecchio ignorante. 🙁

      Se scrivo:
      `\ifdim\differenza = 0pt

      \else

      \fi`
      mi viene dato il seguente errore:

      Illegal unit of measure (pt inserted). =

      Se invece scrivo:
      `\ifdim\differenza pt = 0pt

      \else

      \fi`
      l’errore scompare, ma il comportamento del codice mostra che quell’uguaglianza non viene verificata correttamente.

      Grazie e buona serata,
      Marco.

      Esempio completo, per favore.

      Ciao
      Enrico

    • #80147
      Up
      0
      Down
      ::


      Ciao Enrico,

      chiedo scusa. Ecco un codice che riproduce il comportamento che ho descritto:
      `\documentclass[a4paper,11pt,extrafontsizes,twoside,openright]{memoir}
      \usepackage{etex}
      \usepackage[T1]{fontenc}
      \usepackage[utf8]{inputenc}
      \usepackage[greek,italian]{babel}
      \usepackage{microtype}
      \pagestyle{empty}
      \usepackage{ifthen}

      \usepackage{xcolor}

      \usepackage{tikz}
      % Librerie
      \usetikzlibrary{automata}
      \usetikzlibrary{arrows}
      \usetikzlibrary{backgrounds}
      \usetikzlibrary{calc}
      \usetikzlibrary{hobby}
      \usetikzlibrary{intersections}
      \usetikzlibrary{patterns}
      \usetikzlibrary{positioning}
      \usetikzlibrary{shadows}
      \usetikzlibrary{shapes}
      \usetikzlibrary{through}
      % Pacchetti
      \usepackage{tkz-euclide}
      \usetkzobj{all}

      % TikZ

      %Distanza fra due punti (la lunghezza viene espressa in punti, quindi usare \calcLV pt)
      \makeatletter
      \def\calcLV(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@xa=\pgf@x%
      \pgf@ya=\pgf@y%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@xa}}%
      \FPeval\@temp@b{\pgfmath@tonumber{\pgf@ya}}%
      \FPeval\@temp@sum{(\@temp@a*\@temp@a+\@temp@b*\@temp@b)}%
      \FProot{\FPMathLen}{\@temp@sum}{2}%
      \FPround\FPMathLen\FPMathLen5\relax
      \global\expandafter\edef\csname #3\endcsname{\FPMathLen}
      }
      \makeatother

      % Distanza fra due punti lungo X (la lunghezza viene espressa in punti, quindi usare \calcLVX pt)
      \makeatletter
      \def\calcLVX(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@xa=\pgf@x%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@xa}}%
      \FPround\@temp@a\@temp@a5\relax
      \global\expandafter\edef\csname #3\endcsname{\@temp@a}
      }
      \makeatother

      % Distanza fra due punti lungo Y (la lunghezza viene espressa in punti, quindi usare \calcLVY pt)
      \makeatletter
      \def\calcLVY(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@ya=\pgf@y%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@ya}}%
      \FPround\@temp@a\@temp@a5\relax
      \global\expandafter\edef\csname #3\endcsname{\@temp@a}
      }
      \makeatother

      % MIO NUOVO COMMANDO
      \newcommand{\my}[4]{%
      \coordinate (medio) at ($(#1)!.5!(#2)$);
      \calcLV(#1,medio){dist}
      \coordinate (o) at (0,0);
      \coordinate (temp) at (#3,0);
      \calcLV(o,temp){rag}
      \pgfmathsetmacro{\differenza}{\dist-\rag}
      %
      \ifdim \differenza pt = 0 pt
      \def\abc{2}
      \else
      \def\abc{3}
      \fi
      }

      \begin{document}

      \begin{tikzpicture}[scale=1]

      \draw [help lines] (0,0) grid (3,3);

      \coordinate [label={270:\tiny $O$}] (or) at (0,0);
      \coordinate [label={270:\tiny $A$}] (a) at (50:2);
      \coordinate [label={270:\tiny $B$}] (b) at (50+180:2);

      \my{a}{b}{2}{}

      \fill [black] (a) circle (1.5pt);
      \fill [black] (b) circle (1.5pt);
      \fill [cyan] (\abc,\abc) circle (2pt);

      \end{tikzpicture}

      \end{document}`
      Nel comando [tt]\my[/tt] quello che faccio (cerco di fare) per ora è eseguire questo:

      • Dati due punti, ne creo uno posto esattamente al centro;
      • Misuro la distanza che c’è tra questo e uno dei due punti forniti in ingresso;
      • Converto il raggio inserito in ingresso in pt (sull’efficienza del mio codice credo che ci sia molto da dire);
      • Calcolo la differenza che c’è tra queste due distanze;
      • A seconda del valore di questa differenza eseguo un codice o un altro.

      Nell’esempio che ho riportato [tt]\differenza[/tt] dovrebbe essere nulla, di conseguenza [tt]\abc[/tt] dovrebbe essere pari a [tt]2,[/tt] e quindi il punto azzurro dovrebbe avere coordinate [tt](2,2),[/tt] ma non è così. Cosa sbaglio?

      Grazie e buona giornata,
      Marco.

    • #80148
      Up
      0
      Down
      ::

      Marco87″ post=79354Ciao Enrico,

      chiedo scusa. Ecco un codice che riproduce il comportamento che ho descritto:
      `\documentclass[a4paper,11pt,extrafontsizes,twoside,openright]{memoir}
      \usepackage{etex}
      \usepackage[T1]{fontenc}
      \usepackage[utf8]{inputenc}
      \usepackage[greek,italian]{babel}
      \usepackage{microtype}
      \pagestyle{empty}
      \usepackage{ifthen}

      \usepackage{xcolor}

      \usepackage{tikz}
      % Librerie
      \usetikzlibrary{automata}
      \usetikzlibrary{arrows}
      \usetikzlibrary{backgrounds}
      \usetikzlibrary{calc}
      \usetikzlibrary{hobby}
      \usetikzlibrary{intersections}
      \usetikzlibrary{patterns}
      \usetikzlibrary{positioning}
      \usetikzlibrary{shadows}
      \usetikzlibrary{shapes}
      \usetikzlibrary{through}
      % Pacchetti
      \usepackage{tkz-euclide}
      \usetkzobj{all}

      % TikZ

      %Distanza fra due punti (la lunghezza viene espressa in punti, quindi usare \calcLV pt)
      \makeatletter
      \def\calcLV(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@xa=\pgf@x%
      \pgf@ya=\pgf@y%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@xa}}%
      \FPeval\@temp@b{\pgfmath@tonumber{\pgf@ya}}%
      \FPeval\@temp@sum{(\@temp@a*\@temp@a+\@temp@b*\@temp@b)}%
      \FProot{\FPMathLen}{\@temp@sum}{2}%
      \FPround\FPMathLen\FPMathLen5\relax
      \global\expandafter\edef\csname #3\endcsname{\FPMathLen}
      }
      \makeatother

      % Distanza fra due punti lungo X (la lunghezza viene espressa in punti, quindi usare \calcLVX pt)
      \makeatletter
      \def\calcLVX(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@xa=\pgf@x%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@xa}}%
      \FPround\@temp@a\@temp@a5\relax
      \global\expandafter\edef\csname #3\endcsname{\@temp@a}
      }
      \makeatother

      % Distanza fra due punti lungo Y (la lunghezza viene espressa in punti, quindi usare \calcLVY pt)
      \makeatletter
      \def\calcLVY(#1,#2)#3{%
      \pgfpointdiff{\pgfpointanchor{#1}{center}}%
      {\pgfpointanchor{#2}{center}}%
      \pgf@ya=\pgf@y%
      \FPeval\@temp@a{\pgfmath@tonumber{\pgf@ya}}%
      \FPround\@temp@a\@temp@a5\relax
      \global\expandafter\edef\csname #3\endcsname{\@temp@a}
      }
      \makeatother

      % MIO NUOVO COMMANDO
      \newcommand{\my}[4]{%
      \coordinate (medio) at ($(#1)!.5!(#2)$);
      \calcLV(#1,medio){dist}
      \coordinate (o) at (0,0);
      \coordinate (temp) at (#3,0);
      \calcLV(o,temp){rag}
      \pgfmathsetmacro{\differenza}{\dist-\rag}
      %
      \ifdim \differenza pt = 0 pt
      \def\abc{2}
      \else
      \def\abc{3}
      \fi
      }

      \begin{document}

      \begin{tikzpicture}[scale=1]

      \draw [help lines] (0,0) grid (3,3);

      \coordinate [label={270:\tiny $O$}] (or) at (0,0);
      \coordinate [label={270:\tiny $A$}] (a) at (50:2);
      \coordinate [label={270:\tiny $B$}] (b) at (50+180:2);

      \my{a}{b}{2}{}

      \fill [black] (a) circle (1.5pt);
      \fill [black] (b) circle (1.5pt);
      \fill [cyan] (\abc,\abc) circle (2pt);

      \end{tikzpicture}

      \end{document}`
      Nel comando [tt]\my[/tt] quello che faccio (cerco di fare) per ora è eseguire questo:

      • Dati due punti, ne creo uno posto esattamente al centro;
      • Misuro la distanza che c’è tra questo e uno dei due punti forniti in ingresso;
      • Converto il raggio inserito in ingresso in pt (sull’efficienza del mio codice credo che ci sia molto da dire);
      • Calcolo la differenza che c’è tra queste due distanze;
      • A seconda del valore di questa differenza eseguo un codice o un altro.

      Nell’esempio che ho riportato [tt]\differenza[/tt] dovrebbe essere nulla, di conseguenza [tt]\abc[/tt] dovrebbe essere pari a [tt]2,[/tt] e quindi il punto azzurro dovrebbe avere coordinate [tt](2,2),[/tt] ma non è così. Cosa sbaglio?

      Grazie e buona giornata,
      Marco.

      Alcuni consigli:

      1) puoi fare un po’ di debug creando un nodo e stampando le misure che ti servono. Esempio:

      `\newcommand{\my}[4]{%
      \coordinate (medio) at ($(#1)!.5!(#2)$);
      \calcLV(#1,medio){dist}
      \coordinate (o) at (0,0);
      \coordinate (temp) at (#3,0);
      \calcLV(o,temp){rag}

      \node at (o) {\rag};

      \pgfmathsetmacro{\differenza}{\dist-\rag}

      \node at ($(medio)!0..5!(o)$) {\differenza};

      %
      \ifdim \differenza pt = 0 pt
      \def\abc{2}
      \else
      \def\abc{3}
      \fi
      }`
      È una cosa abbastanza utile che permette di capire se ci sono errori grossi.

      2) Puoi utilizzare funzioni interne di pgf; cerca sul manuale Comparison and logical functions. O anche il pacchetto etoolbox.

      3) A volte è necessario andare a spulciare proprio il codice di pgf per capire alcune cose (parlo in primis per le librerie).. in genere poi uno si deprime perché non è proprio banale, però aiuta. Ad esempio tu stai utilizzando il pacchetto pgfmath. Vai sul terminale e digita (beh.. se utilizzi linux)
      `
      gedit $(kpsewhich pgfmath.sty)
      `
      Ti aprirà il file con il codice del pacchetto. Ora preparati a qualche decina di scatole cinesi.. ogni volta che trovi qualche [tt]\input[/tt] in teoria è qualcosa di interessante. Ad esempio, pgfmath.sty è così composto:

      `
      \input{pgfmath.code.tex}

      \endinput
      ` Apri quindi [tt]pgfmath.code.tex[/tt] con:
      `
      gedit $(kpsewhich pgfmath.code.tex)
      ` Perciò sono di potenziale interesse [tt]pgfmathcalc.code.tex[/tt] e [tt]pgfmathfloat.code.tex[/tt]. Continua ad aprire i vari files come prima; ad esempio, a te serve [tt]pgfmathcalc.code.tex[/tt]:
      `
      gedit $(kpsewhich pgfmathcalc.code.tex)
      ` E così via.

      Ciao
      Claudio

    • #80149
      Up
      0
      Down
      ::


      Ciao Claudio,

      ti ringrazio molto. In effetti non pensavo che si potesse far stampare in quel modo il valore contenuto in [tt]\differenza.[/tt] L’errore era dovuto alla precisione con cui il comando [tt]\calcLV[/tt] eseguiva i calcoli (arrotondando a 5 cifre decimali, se non sbaglio). Visto che nel mio caso la precisione di macchina è dell’ordine di 10-16, ho sostituito questa riga di definizione del comando [tt]\calcLV:[/tt]
      `\FPround\FPMathLen\FPMathLen5\relax`
      con questa:
      `\FPround\FPMathLen\FPMathLen17\relax`
      In questo modo dovrei avere la massima precisione possibile. Così facendo il valore di [tt]\differenza[/tt] visualizzato è di 0.0, e il codice del mio nuovo comando funziona correttamente.

      cfiandrabeh.. se utilizzi linux

      In realtà no; almeno per ora, sono un “leale” sostenitore e utilizzatore di Windows. Comunque cercherò in altri modi quei file e cercherò di seguire i tuoi consigli.

      Grazie mille e buona serata,
      Marco.

    • #80150
      Up
      0
      Down
      ::


      Ciao,

      ho parlato troppo presto. Basta definire il punto [tt](b)[/tt] in questo modo:
      `\coordinate [label={270:\tiny $B$}] (b) at (50+181:2);`
      A questo punto [tt]\differenza[/tt] dovrebbe essere non nulla, ma il valore visualizzato è ancora 0.0. 🙁

      Ciao e grazie,
      Marco.

    • #80151
      Up
      0
      Down
      ::


      Con la macro che davi come esempio, trovo che con la definizione di (b) originale, \differenza si espande a -0.00018 e la macro segue il ramo falso. Altrettanto con la seconda definizione di (b), la differenza stavolta è -0.00233.

      Se chiedo quanto siano \dist e \rag, la risposta è 56.9. Con l’arrotondamento a cinque sole cifre, trovo 56.90530 per \dist e 56.90549 per \rag. Temo che tu stia eseguendo calcoli in modo scorretto dal punto di vista dell’analisi numerica.

      Ciao
      Enrico

    • #80152
      OldClaudio
      Partecipante
        Up
        0
        Down
        ::


        Marco, il fatto che il tuo processore consenta la doppia precisione e ti garantisca 10 cifre significative “esatte” non credo che abbia nulla a che vedere con la precisione con cui pgfmath faccia i conti.

        il pacchetto fp (floating point) apparentemente garantisce un precisione quasi illimitata, ma è lento perché fa i calcoli simulando i contatori floating point di un processore a 64bit con un numero adeguato di contatori interi TeX, e simula i calcoli (che nella CPU vengono fatti con pochi cicli di clock) lavorando con una cifra alla volta come si farebbe a mano su un quaderno quadrettato.

        TeX fa conto solo con quantità intere.

        Calc e le nuove istruzioni di e-TeX (già incorporate in pdftex e xetex, i motori di composizioni “normali, e luatex che però può ricorrere ai calcoli in ambiente Lua) sembra che eseguano i conti con quantità fratte, ma in realtà lavorano con grandezze in punti espresse come valori interi di scaled points, ovvero, il che è in pratica lo stesso, con valori fratti in punti, a a virgola fissa e con 16 cifre binarie nella parte fratta. per la parte intera si arriva solo a 14 cifre binarie, quindi la massima grandezza in valore assoluto è di 16 384pt, anzi, per l’esattezza 16383.99999pt.

        Ne segue che il massimo numero di cifre fratte che si possono usare in aritmetica a virgola fisse, come quella che usano tex, pdftex, xetex, equivale ad una “precisione” di circa 5 o 6 cifre decimali fratte. Non puoi fare di meglio se non usare il pacchetto fp, che però è lentissimo.

        Tutto ciò premesso, i calcoli a virgola fisse implicano degli errori di troncamento o di arrotondamento ad ogni operazione eseguita, per cui le cifre “esatte” alla fine di qualunque conto sono meno di 5 cifre fratte.

        In qualunque corso di programmazione si insegna che i confronti con lo zero sono banali lavorando con gli interi, e sono difficili con i numeri fratti a causa di questi arrotondamenti e troncamenti. Il concetto sostanzialmente è che invece di confrontare qualcosa con lo zero bisogna confrontare il valore assoluto di quel qualcosa con un numero piccolo e non nullo. In termini logico-matematici in aritmetica finita a virgola mobile, bisognerebbe confrontare due quantità relativa, per esempio, detta D la distanza calcolata e D0 una distanza di riferimento bisognerebbe confrontare ` |D/D0| < epsilon` dove epsilon è una quantità positiva sufficientemente piccola, abbastanza minore di uno ma non così piccola da non poter essere rappresentata nell'aritmetica finita di TeX; potrebbe andare bene qualcosa come 0.001pt. Allora il test eseguito è vero se se il primo valore assoluto è minore di epsilon, vale a dire che in quel caso D si può considerare trascurabile rispetto a D0, quindi, scelto D0 in modo adeguato, ma in modo che sia paragonabile col il massimo delle addendi algebrici usati nei calcoli precedenti, allora D si può considerare nullo entro la tolleranza relativa stabilita con epsilon. Il fatto che possa impostare il limite del troncamento ad un valore qualsiasi con i comandi che presenti, non mi sembra una cosa da fare e lo considererei un bug di pgfmath se consente di farlo. Non sperare di poter mai ottenere differenze nulle fra distanze lavorando con i programmi del sistema TeX; Non ci riusciresti nemmeno se usassi triangoli pitagorei; nel momento in cui calcoli, per esempio, la radice quadrata di x al quadrato più y al quadrato dove x=3 e y=4, non sperare di ottenere 5; al meglio potrai ottenere 4.99973 oppure 5,00013 o cose del genere. Non voglio deprimerti con queste indicazioni, ma sperare di poter ottenere dei valori nulli eseguendo delle differenze fra numeri fratti è praticamente impossibile.

      • #80153
        Up
        0
        Down
        ::


        Ciao,

        grazie Enrico e grazie Claudio! Ho capito quello che mi avete detto (almeno credo!). Cercherò di implementare diversamente il mio codice (o di farne a meno).

        Grazie ancora e buona giornata, 🙂
        Marco.

    Visualizzazione 9 filoni di risposte
    • Devi essere connesso per rispondere a questo topic.

    Go to top