Go: Converting float64 to int with multiplier - go

I want to convert a float64 number, let's say it 1.003 to 1003 (integer type). My implementation is simply multiply the float64 with 1000 and cast it to int.
package main
import "fmt"
func main() {
var f float64 = 1.003
fmt.Println(int(f * 1000))
}
But when I run that code, what I got is 1002 not 1003. Because Go automatically stores 1.003 as 1.002999... in the variable. What is the correct approach to do this kind of operation on Golang?

Go spec: Conversions:
Conversions between numeric types
When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
So basically when you convert a floating-point number to an integer, only the integer part is kept.
If you just want to avoid errors arising from representing with finite bits, just add 0.5 to the number before converting it to int. No external libraries or function calls (from standard library) required.
Since float -> int conversion is not rounding but keeping the integer part, this will give you the desired result. Taking into consideration both the possible smaller and greater representation:
1002.9999 + 0.5 = 1003.4999; integer part: 1003
1003.0001 + 0.5 = 1003.5001; integer part: 1003
So simply just write:
var f float64 = 1.003
fmt.Println(int(f * 1000 + 0.5))
To wrap this into a function:
func toint(f float64) int {
return int(f + 0.5)
}
// Using it:
fmt.Println(toint(f * 1000))
Try them on the Go Playground.
Note:
Be careful when you apply this in case of negative numbers! For example if you have a value of -1.003, then you probably want the result to be -1003. But if you add 0.5 to it:
-1002.9999 + 0.5 = -1002.4999; integer part: -1002
-1003.0001 + 0.5 = -1002.5001; integer part: -1002
So if you have negative numbers, you have to either:
subtract 0.5 instead of adding it
or add 0.5 but subtract 1 from the result
Incorporating this into our helper function:
func toint(f float64) int {
if f < 0 {
return int(f - 0.5)
}
return int(f + 0.5)
}

As Will mentions, this comes down to how floats are represented on various platforms. Essentially you need to round the float rather than let the default truncating behavior to happen. There's no standard library function for this, probably because there's a lot of possible behavior and it's trivial to implement.
If you knew you'd always have errors of the sort described, where you're slightly below (1299.999999) the value desired (1300.00000) you could use the math library's Ceil function:
f := 1.29999
n := math.Ceil(f*1000)
But if you have different kinds of floating error and want a more general sorting behavior? Use the math library's Modf function to separate the your floating point value by the decimal point:
f := 1.29999
f1,f2 := math.Modf(f*1000)
n := int(f1) // n = 1299
if f2 > .5 {
n++
}
fmt.Println(n)
You can run a slightly more generalized version of this code in the playground yourself.

This is probably likely a problem with floating points in general in most programming languages though some have different implementations than others. I wouldn't go into the intricacies here but most languages usually have a "decimal" approach either as a standard library or a third party library to get finer precision.
For instance, I've found the inf.v0 package largely useful. Underlying the library is a Dec struct that holds the exponents and the integer value. Therefore, it's able to hold 1.003 as 1003 * 10^-3. See below for an example:
package main
import (
"fmt"
"gopkg.in/inf.v0"
)
func main() {
// represents 1003 * 10^-3
someDec := inf.NewDec(1003, 3)
// multiply someDec by 1000 * 10^0
// which translates to 1003 * 10^-3 * 1000 * 10^0
someDec.Mul(someDec, inf.NewDec(1000, 0))
// inf.RoundHalfUp rounds half up in the 0th scale, eg. 0.5 rounds to 1
value, ok := someDec.Round(someDec, 0, inf.RoundHalfUp).Unscaled()
fmt.Println(value, ok)
}
Hope this helps!

Related

How to keep precision for big numbers in golang when converting from float to big.Int

I have an input that could be a very big or a very small float and need to convert it to big.Int, but for some reason, there is some precision loss.
I understand that this should happen for very small numbers, but why does it happen for a big number, and how to avoid it?
https://go.dev/play/p/AySnKAikSRx
All positive integers up to 9007199254740992 can be represented in a float64 without any loss of precision. Anything higher, you run the risk of precision loss, which is happening in your case.
To give a basic idea of why..
Say we're inventing an extremely compact scheme for representing floating point numbers using the following formula:
m.mm * 10^+-e
.. where:
e = exponent, [1-9]
m.mm = mantissa [0.01-9.99]
With this, we can figure out what range of values can be represented:
lowest = 0.01 * 10^-9 = 0.00000000001
highest = 9.99 * 10^9 = 9990000000
So that's a pretty decent range of numbers.
We can represent a fair few positive integers without any difficulty, e.g.
1 = 1.00 * 10^0
2 = 2.00 * 10^0
3 = 3.00 * 10^0
⋮
10 = 1.00 * 10^1
11 = 1.10 * 10^1
12 = 1.20 * 10^1
⋮
100 = 1.00 * 10^2
101 = 1.01 * 10^2
102 = 1.02 * 10^2
⋮
999 = 9.99 * 10^2
The problem starts when we exceed 9.99 * 10^2. It's not an issue to represent 1000:
1000 = 1.00 * 10^3
But how do represent 1001? The next possible value is
1.01 * 10^3 = 1010
Which is +9 loss of precision, so we have to settle on 1.00 * 10^3 with -1 loss of precision.
The above is in essence how this plays out with float64, except in base 2 and with a 52 bit mantissa in play. With all 52 bits set, and then adding one, the value is:
1.0 * 2^53 = 9007199254740992
So all positive integers up to this value can be represented without precision loss. Integers higher than this may incur precision loss - it very much depends on the value.
Now, the value referenced in your Go code:
var x float64 = 827273999999999954
There is no way to represent this exact value as a float64.
package main
import (
"fmt"
)
func main() {
var x float64 = 827273999999999954
fmt.Printf("%f\n", x)
}
yields..
827274000000000000.000000
So essentially precision is lost by the time x is initialized. But when does that occur? If we run..
$ go build -o tmp
$ go tool objdump tmp
And search for TEXT main.main(SB), we can find the instruction:
main.go:10 0x108b654 48b840d5cba322f6a643 MOVQ $0x43a6f622a3cbd540, AX
So 0x43a6f622a3cbd540 is being set into AX - this is our float64 value.
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("float: %f\n", math.Float64frombits(0x43a6f622a3cbd540))
}
prints
float: 827274000000000000.000000
So the precision has essentially been lost at compile time (which makes sense). So on the line of code with big.NewFloat(x).Int(nil), the value being passed as x is 827274000000000000.000000
how to avoid it?
With the code you've provided, there is no way.
If you're able to represent the value as an integer..
package main
import (
"fmt"
"math/big"
)
func main() {
var x uint64 = 827273999999999954
bf := (&big.Float{}).SetUint64(x)
fmt.Println(bf)
}
yields
8.27273999999999954e+17
which is the value you're expecting. Or alternatively via a string:
package main
import (
"fmt"
"math/big"
)
func main() {
var x string = "827273999999999954"
bf, ok := (&big.Float{}).SetString(x)
if !ok {
panic("failed to set string")
}
fmt.Println(bf)
}

How can I convert float(-1.0) to int -1 correctly?

my code:
step := 10.0
precision := int(math.Log10(1/step))
fmt.PrintLn(precision)
I want precision == -1 but got 0...
Float to integer conversion truncates, so if your float number is e.g. 0.99, converting it to integer will be 0 and not 1.
If you want to round to an integer, you may simply use math.Round() (which returns float64 so you still need to manually convert to int, but the result will be what you expect):
step := 10.0
precision := int(math.Log10(1 / step))
fmt.Println(precision)
precision = int(math.Round(math.Log10(1 / step)))
fmt.Println(precision)
This will output (try it on the Go Playground):
0
-1
If you want to round to a specific fraction (and not to integer), see Golang Round to Nearest 0.05.

IEEE 754 binary floating-point numbers imprecise for money

I have a problem when I use math.Floor with a floating-point variable (round down/truncate the precision part). How can I do it correctly?
package main
import (
"fmt"
"math"
)
func main() {
var st float64 = 1980
var salePrice1 = st * 0.1 / 1.1
fmt.Printf("%T:%v\n", salePrice1, salePrice1) // 179.9999
var salePrice2 = math.Floor(st * 0.1 / 1.1)
fmt.Printf("%T:%v\n", salePrice2, salePrice2) // 179
}
Playground: https://play.golang.org/p/49TjJwwEdEJ
Output:
float64:179.99999999999997
float64:179
I expect the output of 1980 * 0.1 / 1.1 to be 180, but the actual output is 179.
The original question:
Incorrect floor number in golang
I have problem when use Math.Floor with float variable (round
down/truncate the precision part). How can i do it correctly?
package main
import (
"fmt"
"math"
)
func main() {
var st float64 = 1980
var salePrice1 = st * 0.1 / 1.1
fmt.Printf("%T:%v\n", salePrice1, salePrice1)
var salePrice2 = math.Floor(st * 0.1 / 1.1)
fmt.Printf("%T:%v\n", salePrice2, salePrice2)
}
I expect the output of 1980 * 0.1 / 1.1 to be 180, but the actual
output is 179.”
Playground:
Output:
float64:179.99999999999997
float64:179
The XY problem is asking about your attempted solution rather than your actual problem: The XY Problem.
Clearly, this is a money calculation for salePrice1. Money calculations use precise decimal calculations, not imprecise binary floating-point calculations.
For money calculations use integers. For example,
package main
import "fmt"
func main() {
var st int64 = 198000 // $1980.00 as cents
fmt.Printf("%[1]T:%[1]v\n", st)
fmt.Printf("$%d.%02d\n", st/100, st%100)
var n, d int64 = 1, 11
fmt.Printf("%d, %d\n", n, d)
var salePrice1 int64 = (st * n) / d // round down
fmt.Printf("%[1]T:%[1]v\n", salePrice1)
fmt.Printf("$%d.%02d\n", salePrice1/100, salePrice1%100)
var salePrice2 int64 = ((st*n)*10/d + 5) / 10 // round half up
fmt.Printf("%[1]T:%[1]v\n", salePrice2)
fmt.Printf("$%d.%02d\n", salePrice2/100, salePrice2%100)
var salePrice3 int64 = (st*n + (d - 1)) / d // round up
fmt.Printf("%[1]T:%[1]v\n", salePrice1)
fmt.Printf("$%d.%02d\n", salePrice3/100, salePrice3%100)
}
Playground: https://play.golang.org/p/HbqVJUXXR-N
Output:
int64:198000
$1980.00
1, 11
int64:18000
$180.00
int64:18000
$180.00
int64:18000
$180.00
References:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
How should we calc money (decimal, big.Float)
General Decimal Arithmetic
Try this:
st := 1980.0
f := 0.1 / 1.1
salePrice1 := st * f
salePrice2 := math.Floor(salePrice1)
fmt.Println(salePrice2) // 180
It is a big topic:
For accounting systems: the answer is Floating point error mitigation.
(Note: one mitigation technique is to use int64, uint64, or big.Int)
And see:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
https://en.wikipedia.org/wiki/IEEE_floating_point
Let's start with:
fmt.Println(1.0 / 3.0) // 0.3333333333333333
IEEE 754 binary representation:
fmt.Printf("%#X\n", math.Float64bits(1.0/3.0)) // 0X3FD5555555555555
IEEE 754 binary representation of 1.1:
fmt.Printf("%#X\n", math.Float64bits(1.1)) // 0X3FF199999999999A
fmt.Printf("%#X\n", math.Float64bits(st*0.1/1.1)) // 0X40667FFFFFFFFFFF
Now, let:
st := 1980.0
f := 0.1 / 1.1
IEEE 754 binary representation of f is:
fmt.Printf("%#X\n", math.Float64bits(f)) // 0X3FB745D1745D1746
And:
salePrice1 := st * f
fmt.Println(salePrice1) // 180
fmt.Printf("%#X\n", math.Float64bits(salePrice1)) // 0X4066800000000000
salePrice2 := math.Floor(salePrice1)
fmt.Printf("%#X\n", math.Float64bits(salePrice2)) // 0X4066800000000000
Working with the floating-point numbers on the computer is not same as with pen and paper (Floating-point calculation errors):
var st float64 = 1980
var salePrice1 = st * 0.1 / 1.1
fmt.Println(salePrice1) // 179.99999999999997
salePrice1 is 179.99999999999997 not 180.0 so integer value less than or equal to 179.99999999999997 is 179:
See documents for func Floor(x float64) float64:
Floor returns the greatest integer value less than or equal to x.
See:
fmt.Println(math.Floor(179.999)) // 179
fmt.Println(math.Floor(179.5 + 0.5)) // 180
fmt.Println(math.Floor(179.999 + 0.5)) // 180
fmt.Println(math.Floor(180.0)) // 180
Some relevant QAs:
Golang floating point precision float32 vs float64
How to change a float64 number to uint64 in a right way?
Golang converting float64 to int error
Is floating point math broken?
Go float comparison
What does "%b" do in fmt.Printf for float64 and what is Min subnormal positive double in float64 in binary format?
Is there any standard library to convert float64 to string with fix width with maximum number of significant digits?
fmt.Printf with width and precision fields in %g behaves unexpectedly
Why is there a difference between floating-point multiplication with literals vs. variables in Go?
Golang Round to Nearest 0.05

Why fmt.Println(float64(1/2)) shows 0?

package main
import (
"fmt"
)
func main() {
fmt.Println(float64(1/2))
}
Why it prints: 0
Playground link: https://play.golang.org/p/KGgao6n8lTA
Is it because fmt.Println precision is low?
The order of operations here is: 1/2 = 0 (integer division truncates decimal places) followed by float64(0) = 0, then fmt.Println(0).
So in short: the integer division is truncated to 0. Everything else works fine.
As #Amadan commented, you can force a floating point division by casting one of the integers, i.e. float64(1) / 2 = 0.5.

Project Euler 16 - Help in solving it

I'm solving Project Euler problem 16, I've ended up with a code that can logically solve it, but is unable to process as I believe its overflowing or something? I tried int64 in place of int but it just prints 0,0. If i change the power to anything below 30 it works, but above 30 it does not work, Can anyone point out my mistake? I believe its not able to calculate 2^1000.
// PE_16 project main.go
package main
import (
"fmt"
)
func power(x, y int) int {
var pow int
var final int
final = 1
for pow = 1; pow <= y; pow++ {
final = final * x
}
return final
}
func main() {
var stp int
var sumfdigits int
var u, t, h, th, tth, l int
stp = power(2,1000)
fmt.Println(stp)
u = stp / 1 % 10
t = stp / 10 % 10
h = stp / 100 % 10
th = stp / 1000 % 10
tth = stp / 10000 % 10
l = stp / 100000 % 10
sumfdigits = u + t + h + th + tth + l
fmt.Println(sumfdigits)
}
Your approach to this problem requires exact integer math up to 1000 bits in size. But you're using int which is 32 or 64 bits. math/big.Int can handle such task. I intentionally do not provide a ready made solution using big.Int as I assume your goal is to learn by doing it by yourself, which I believe is the intent of Project Euler.
As noted by #jnml, ints aren't large enough; if you wish to calculate 2^1000 in Go, big.Ints are a good choice here. Note that math/big provides the Exp() method which will be easier to use than converting your power function to big.Ints.
I worked through some Project Euler problems about a year ago, doing them in Go to get to know the language. I didn't like the ones that required big.Ints, which aren't so easy to work with in Go. For this one, I "cheated" and did it in one line of Ruby:
Removed because I remembered it was considered bad form to show a working solution, even in a different language.
Anyway, my Ruby example shows another thing I learned with Go's big.Ints: sometimes it's easier to convert them to a string and work with that string than to work with the big.Int itself. This problem strikes me as one of those cases.
Converting my Ruby algorithm to Go, I only work with big.Ints on one line, then it's easy to work with the string and get the answer in just a few lines of code.
You don't need to use math/big. Below is a school boy maths way of doubling a decimal number as a hint!
xs holds the decimal digits in least significant first order. Pass in a pointer to the digits (pxs) as the slice might need to get bigger.
func double(pxs *[]int) {
xs := *pxs
carry := 0
for i, x := range xs {
n := x*2 + carry
if n >= 10 {
carry = 1
n -= 10
} else {
carry = 0
}
xs[i] = n
}
if carry != 0 {
*pxs = append(xs, carry)
}
}

Resources