Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This code

\documentclass{report}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

\draw[dashed] (1,-1) -- (1,2) node[above] {$1$};
\draw[dashed] (2,-1) -- (2,2) node[above] {$2$};

\foreach \x in {1,1.1,...,2}
    {\draw[fill = yellow] (\x,1) circle (0.05);}

\foreach \x in {1,1.2,...,2}
    {\draw[fill = red] (\x,0) circle (0.05);}   

\end{tikzpicture}   

\end{document}

produces the following output.

enter image description here

The first \foreach produces the yellow circles. There is no circle at 2, the last element in the set {1,1.1,...,2} over which it iterates.

The second \foreach produces the red circles. There is a circle at 2, the last element in the set {1,1.2,...,2} over which it iterates.

Why do these two commands behave differently?

share|improve this question
    
@Jubobs It is the rounding. If you add that as an answer I'll accept it, otherwise I'll answer myself. Thanks. –  in_wolfram_we_trust May 31 '13 at 10:28
4  
With all answers given, there is another way to enforce 2.0 by looping over integers: \foreach \xx[evaluate={\x=\xx/10}] in {10,11,...,20} –  Qrrbrbirlbel May 31 '13 at 13:27

3 Answers 3

up vote 30 down vote accepted
+200

Short answer

The problem illustrated by your example is due to round-off error. See the TikZ/PGF documentation (section 56 in v2.10, p.505; or section 83 in v3.0, p.910):

[...] for fractional steps that are not multiples of 2^{-n} for some small n, rounding errors can occur pretty easily. Thus, in \foreach \x in {0,0.1,...,0.5} {\x, }, 0.5 should probably be replaced by 0.501 for robustness.

In your particular example, using a value slightly greater than 2 (such as 2.0001) for the last element of the list, e.g.

\foreach \x in {1,1.1,...,2.0001}

gives some margin for round-off error and solves the problem.

enter image description here

However, a preferable way of obviating that round-off-error problem is, as suggested in g.kov's answer, percusse's comment, and Qrrbrbirlbel's comment, to iterate over integers and compute the value of interest elsewhere, either like so

\foreach \xx[evaluate={\x=\xx/10}] in {10,11,...,20}

or inside the body of \foreach:

\foreach \xx in {10,11,...,20}{
    \pgfmathsetmacro\x{.1*\xx}
    ...
}

For a more detailed explanation, see below.


TeX's fixed-point number system

Some real numbers have an infinite binary expansion, i.e. their fractional part contains an infinitely repeating bit pattern. For instance,

0.1 (in decimal form)

corresponds to

0.0001100110011001100110011... (in binary form)

with the bit pattern "0011" repeating "forever". Since finite-precision machine numbers only allocate a limited number of bits for the fractional part of a given real number, they cannot store that infinite expansion and some rounding has to occur. TeX uses a 32-bit fixed-point number representation system, where

  • 1 bit is used to flag an overflow (if any),
  • 1 bit is used to code the sign of the number,
  • 14 bits are used for the integral part of the number,
  • 16 bits are used for the fractional part of the number.

It follows that

enter image description here

are the largest and smallest (respectively) TeX numbers. By "TeX numbers", I mean numbers that can be represented exactly using the TeX number representation system. Any real number larger than about 16383.99999 in magnitude will cause an overflow. Moreover, a finite-precision number system can only represent a finite number of real numbers, which means that some real numbers between -16383.99999 +16383.99999 cannot be exactly represented by TeX numbers; those real numbers can be represented only approximately by TeX numbers.

I'm not sure what the rounding rule is in TeX but it seems that a (positive) real number gets

  • rounded up if the 17th bit to the right of the binary point (in its binary expansion) is 1,
  • truncated if the 17th bit to the right of the binary point (in its binary expansion) is 0,

In the case of 0.1, the 17th bit to the right of the binary point is 1; therefore, 0.1 gets rounded up to the next TeX number, i.e.

0.0001100110011010 (in binary form)

(Compare to the exact value of 0.1 written above.) In summary, the number manipulated by TeX is, not 0.1, but a number slightly larger than 0.1. Conversely

0.2 (in decimal form)

corresponds to

0.001100110011001100110011... (in binary form)

with the pattern "0011" repeating "forever" after the binary point. The 17th bit to the right of the binary point is 0; therefore, 0.2 gets truncated/rounded down to the preceding TeX number i.e.

0.001100110011011 (in binary form)

(Compare to the exact value of 0.2 written above.) In summary, the number manipulated by TeX is, not 0.2, but a number slightly smaller than 0.2.

Mechanics of \foreach

Compile

\documentclass{article}
\usepackage{pgffor}
\begin{document}
\noindent
\foreach \x in {1,1.1,...,2}{\x\\ } \    \foreach \x in {1,1.2,...,2}{\x\\ }
\end{document}

and you get

enter image description here

To make sense of this output, you need to know a bit about how \foreach's dots notation works. When \foreach encounters a ... element in its list argument, as in \foreach \i in {x,y,...,z}, it computes the difference d=y-x to fill in the "missing values". According to the TikZ/PGF manual (p.505),

the part of the list reading x,y,...,z is replaced by x, x + d, x + 2d, x + 3d, ..., x + md, where the last dots are semantic dots, not syntactic dots. The value m is the largest number such that x + md <= z if d is positive or such that x + md >= z if d is negative.

In the case of \foreach \x in {1,1.1,...,2}, the difference between the first two items is the TeX number 0.1, which, as explained above, is slightly larger than the real number 0.1. Therefore, the largest m such that 1+0.1*m <= 2 is 9. As a result, the final value visited by the loop is, not 2, but a TeX number slightly larger than the mathematical number 1.9.

In the case of \foreach \x in {1,1.2,...,2}, the difference between the first two items is the TeX number 0.2, which, as explained above, is slightly smaller than the real number 0.2. Therefore, the largest m such that 1+0.2*m <= 2 is 5. As a result, the final value visited by the loop is, not exactly 2, but a TeX number only very slightly smaller than 2, hence the more satisfactory output obtained in this case compared to \foreach \x in {1,1.1,...,2}.

References

share|improve this answer
    
TeX doesn't use floating point, but only integer numbers. On the other hand, PGF implements floating point (on top of TeX's integer arithmetic). I don't think TeX's arithmetic has a role in this problem. As far as I know, TeX always truncates when doing divisions. –  egreg May 31 '13 at 12:57
    
@egreg Doesn't TeX use fixed-point numbers as well as integers? What about section 3 of the article I link to? –  Jubobs May 31 '13 at 12:59
    
We were talking about two different things. ;-) OK, the "fixed-point" model is what's used for representing lengths in the context of \the<dimension>. I believe that PGF has its own floating point system, because with TeX's representation only four decimal digits are accurate (and TeX displays at most five). –  egreg May 31 '13 at 13:07
    
@egreg Subsection 61.3 (about the PGF math engine) in the PGF/TikZ manual states: Currently, the mathematical algorithms are all implemented in TEX. Also, section 62 states: all calculations must not exceed ±16383.99999 at any point, because the underlying computations rely on TEX dimensions. I think the floating-point unit (fpu) you're referring to is not loaded by default but has to be activated explicitly; see section 36. –  Jubobs May 31 '13 at 13:12
2  
@egreg It is indeed the case. You can turn on the fpu library for more precision if you really need it(but mostly you regret that you did that for simple things as this, because then you need to parse the float representation whereever required). Otherwise it is the regular TeX arithmetic. The better approach is to run over the integers and multiply the value inside the body of \foreach. –  percusse May 31 '13 at 13:17

Both commands behave exactly the same. The rounding error is the reason that the terminal value is "missed" in the first case. This check

\documentclass{report}    
\usepackage{tikz}    
\begin{document}    
\foreach \x in {1,1.1,...,2}
    {\number\x\ }    
\foreach \x in {1,1.2,...,2}
    {\number\x\ }    
\end{document}

results in

1 1.1 1.20001 1.30002 1.40002 1.50003 1.60004 1.70004 1.80005 1.90005
1 1.2 1.4 1.59999 1.79999 1.99998

so, the next value would be greater than 2 in the first case.

The rule of thumb is: never ever use non-integer for the step value in a loop.

share|improve this answer

Rounding errors, yes, but it doesn't explain everything. \foreach doesn't use the upper bound of the ellipsis (...) list but rather relies on comparing the computed value with the upper bound by means of the delta (increment). \newforeach enforces the upper bound, in the belief that the user intended the upper bound to be used as an explicit point/value.

\documentclass{article}
\usepackage{tikz}
\usepackage{loops}[2013/05/01]

\begin{document}
\begin{tikzpicture}
\draw[dashed] (1,-1) -- (1,2) node[above] {$1$};
\draw[dashed] (2,-1) -- (2,2) node[above] {$2$};
\newforeach \x in {1,1.1,...,2}{
  \draw[fill = yellow] (\x,1) circle (0.05);
}
\newforeach \x in {1,1.2,...,2}{
  \draw[fill = red] (\x,0) circle (0.05);
}   
\end{tikzpicture}
\end{document}
share|improve this answer
1  
+1 In my solution, I assume that "my readers" are familiar with how the mechanics of \foreach. Thanks for adding some info about that. –  Jubobs May 31 '13 at 12:54
4  
Unfortunately, this draws the last red node of the second loop twice: first at \x = 1.99998 and then at \x = 2. In this case, we don’t notice it as the difference is small enough but I wouldn’t rely on that (opacity, scaling that makes it visible, etc). –  Qrrbrbirlbel Aug 29 '13 at 6:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.