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

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.

Related

How to map two value ranges in tinygo

I'm using Golang to program a arduino uno with tinygo. I am trying to map two value ranges.
One is an encoder with a range between 0-1000 and the other is tinygo's ADC range between 0-65535. I am reading the ADC range and need to covert it to the range of 0-1000 (encoder).
I have tried several things but the basic issue that I'm running into is data types. The below formula for example equals 0:
var encoderValue uint16 = 35000
float := float64(1000/65535) * float(encoderValue)
1000/65535 is an integer division and will result in 0. It doesn't matter if you convert the result to float64, then it'll be 0.0.
Use floating point constant(s):
var encoderValue uint16 = 35000
x := float64(1000.0/65535) * float64(encoderValue)
fmt.Println(x)
This will output (try it on the Go Playground):
534.0657663843748

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

big.Float GetString without rounding

I want to see the result: "3891113451447590234" without "3891113451447590400"
bigI,_ := big.NewInt(0).SetString("3891113451447590234", 10)
bigF := big.NewFloat(0).SetInt(bigI)
fmt.Println(bigF)
fmt.Println(bigF.String())
fmt.Println(bigF.SetMode(big.AwayFromZero).Text('f', 8))
fmt.Println(bigF.SetMode(big.AwayFromZero).Text('g', 20))
3.8911134514475904e+18
3.891113451e+18
3891113451447590400.00000000
3891113451447590400
The big.NewFloat function sets the default precision to 53.
NewFloat allocates and returns a new Float set to x, with precision 53 and rounding mode ToNearestEven. NewFloat panics with ErrNaN if x is a NaN.
If you want to set values with higher precision, you can set the precision directly, or you can start with precision 0 using a zero value of big.Float which determines the required precision when the value is first set.
f1, _, _ := new(big.Float).SetPrec(128).SetMode(big.ToNearestEven).Parse("3891113451447590234", 10)
// equivalent to
// big.ParseFloat("3891113451447590234", 10, 128, big.ToZero)
fmt.Println(f1)
// 3.891113451447590234e+18
i, _ = new(big.Int).SetString("3891113451447590234", 10)
f2 = new(big.Float).SetInt(i)
fmt.Println(f2)
// 3.891113451447590234e+18

Go: Converting float64 to int with multiplier

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!

Convert uint64 to int64 without loss of information

The problem with the following code:
var x uint64 = 18446744073709551615
var y int64 = int64(x)
is that y is -1. Without loss of information, is the only way to convert between these two number types to use an encoder and decoder?
buff bytes.Buffer
Encoder(buff).encode(x)
Decoder(buff).decode(y)
Note, I am not attempting a straight numeric conversion in your typical case. I am more concerned with maintaining the statistical properties of a random number generator.
Your conversion does not lose any information in the conversion. All the bits will be untouched. It is just that:
uint64(18446744073709551615) = 0xFFFFFFFFFFFFFFFF
int64(-1) = 0xFFFFFFFFFFFFFFFF
Try:
var x uint64 = 18446744073709551615 - 3
and you will have y = -4.
For instance: playground
var x uint64 = 18446744073709551615 - 3
var y int64 = int64(x)
fmt.Printf("%b\n", x)
fmt.Printf("%b or %d\n", y, y)
Output:
1111111111111111111111111111111111111111111111111111111111111100
-100 or -4
Seeing -1 would be consistent with a process running as 32bits.
See for instance the Go1.1 release notes (which introduced uint64)
x := ^uint32(0) // x is 0xffffffff
i := int(x) // i is -1 on 32-bit systems, 0xffffffff on 64-bit
fmt.Println(i)
Using fmt.Printf("%b\n", y) can help to see what is going on (see ANisus' answer)
As it turned out, the OP wheaties confirms (in the comments) it was run initially in 32 bits (hence this answer), but then realize 18446744073709551615 is 0xffffffffffffffff (-1) anyway: see ANisusanswer;
The types uint64 and int64 can both represent 2^64 discrete integer values.
The difference between the two is that uint64 holds only positive integers (0 thru 2^64-1), where as int64 holds both negative and positive integers using 1 bit to hold the sign (-2^63 thru 2^63-1).
As others have said, if your generator is producing 0xffffffffffffffff, uint64 will represent this as the raw integer (18,446,744,073,709,551,615) whereas int64 will interpret the two's complement value and return -1.

Resources