<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>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传