<p>Hi,</p> <p>I try to round 2100.825 to 2100.83 but get always 2100.82. Here is the code </p> <pre><code>package main import ( "fmt" "math" ) func main() { fmt.Println("Expecting 2100.83, but was:", round100(2100.825)) } func round100(x float64) float64 { a := x * 100 _, rem := math.Modf(a) fmt.Println("frac:", rem) if rem >= 0.5 { fmt.Println("ceil") a = math.Ceil(a) } else { fmt.Println("floor") a = math.Floor(a) } return a / 100 } </code></pre> <p>If I modify the number to 1100.825 it is properly rounded to 1100.83.</p> <p>Where is the flaw? And what is the correct implementation of round100?</p> <hr/>**评论:**<br/><br/>ElliottStoneham: <pre><p>The problem is not with your program, it is that the float64 representation of 2100.825 is not exact. Try:</p> <pre><code>fmt.Printf("%4.24g",2100.825) </code></pre></pre>coldDragonBreath: <pre><p>For the lazy <a href="https://play.golang.org/p/YBJeUP73tp" rel="nofollow">playground</a>:</p> <p><em>fmt.Printf("%4.24g",2100.825)</em> yields <em>2100.82499999999981810106</em></p></pre>pdq: <pre><p>Are you trying to work with currency or decimal values?</p> <p>If so, I'd strongly suggest to stay away from floating point (which rounds) and use a decimal-type struct instead, like <a href="https://github.com/shopspring/decimal">https://github.com/shopspring/decimal</a></p></pre>a4st: <pre><p>Yes, you are right, I'm working with currency. I will try out the library you suggest. But I'm still interested why is it only about this "magic" number?</p></pre>coldDragonBreath: <pre><p>It's the classic Programming showcase of why: (0.1 + 0.2 != 0.3)</p> <p>If you want to get into the maths behind it, google and search "IEEE 754"</p></pre>thockin: <pre><p>Yeah, don't use FP for currency. Use the smallest integral unit you care about. E.g. cents or millicents or microcents or ..</p></pre>tmornini: <pre><p>A good decimal package is a far better idea than using integers.</p></pre>a4st: <pre><p>What about using math/big.Rat?</p></pre>joushou: <pre><p>TL;DR: Never use floating point if rounding errors (small or large) are a concern. Accurate math requires integers and safe integer operations (addition, subtraction, multiplication).</p> <p>Floating point can only represent some values precisely due to its design. In its simplified form, a floating point value is calculated as s * b<sup>e,</sup> where s is the significand, e is the exponent and b is the base. In our case, the base is two, turning it the equation into s * 2<sup>e.</sup> You can't express the value 1/3 using when the base is 2, although you can get awfully close if with some crazy large and obscure values of s and e.</p> <p>Also note that using integer operands is not enough, but you may only use "safe" integer operations if you need to maintain an accurate value. A division of integer operations always rounds towards zero—that is, 2/3 yields 0—which is basically a guaranteed rounding error. Likewise, casting a floating point value to an integer also round towards zero.</p></pre>tmornini: <pre><blockquote> <p>Never use floating point if rounding errors (small or large) are a concern. Accurate math requires integers and safe integer operations</p> </blockquote> <p>Or, better yet, a decimal package.</p></pre>tmornini: <pre><p>The way floating point values work is that they take a big number range, and break it up into as many buckets as there are bits in the representation.</p> <p>So, as a thought experiment -- because this is NOT the way it's actually done, simplification to aid in understanding -- imagine you had a 3 bit floating point number that represented values between -75 and 75.</p> <p>The first bit represent positive or negative. I'll use 0 for positive values, 1 for negative values.</p> <p>The ONLY values representable would be:</p> <pre><code>011 = 75.0 010 = 50.0 001 = 25.0 000 = 0.0 100 = -18.75 101 = -37.5 110 = -56.25 111 = -75.0 </code></pre> <p>And here's the point: those are the ONLY numbers that can be represented in this "floating point" system.</p> <p>If you use 32 bit floats, 2<sup>32</sup> numbers are representable. With 64 bit floats, 2<sup>64</sup> numbers are representable.</p> <p>As far as floats are concerns, all numbers that do not happen to be represented, simply don't exist!</p> <p>Here's a concrete example in the <a href="https://play.golang.org/p/-oENvtuLSB" rel="nofollow">Go Playground</a></p></pre>mjibson: <pre><p>In addition to the shopspring package already posted, <a href="https://godoc.org/github.com/cockroachdb/apd" rel="nofollow">apd</a> is another decimal package that does <a href="https://godoc.org/github.com/cockroachdb/apd#example-Context-Quantize" rel="nofollow">similar things</a>, but also supports various <a href="https://godoc.org/github.com/cockroachdb/apd#pkg-constants" rel="nofollow">kinds of rounding</a>.</p></pre>gobdgobd: <pre><p>Seems to work for me if I add this after your Modf line</p> <pre><code>rem, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", rem), 64) </code></pre></pre>
