Need advice: cast(integer, float)
Need advice: cast(integer, float)
In C you get the floor and fractional of a float or double (assuming it never is negative) in simple and fast ways:
int integral = (int)fl;
float fractional = fl - integral;
how do you do this in freebasic?
every time i work with numbers in freebasic i have to be extra careful to avoid rounding errors
because of that **** qbasic where they thought it was a good idea to round everyones numbers
print cast(integer, 0.9) '' 1
dim as integer t = 0.9
print t '' 1
is there any way to turn this "feature" off? and why is it even there?
no one else feeling like they are coding with one eye watching for the awesome rounding feature?
(i'm really sorry, but i just can't get used to this)
my code is usually littered with int(x) because of this... it makes me teary-eyed :)
anyways, before i go out on a venting mission again, how can you get float to int with floor, without doing extra work?
what is the actual assembly for this?
edit: here are some solutions:
http://stackoverflow.com/questions/2352 ... s-to-floor
int integral = (int)fl;
float fractional = fl - integral;
how do you do this in freebasic?
every time i work with numbers in freebasic i have to be extra careful to avoid rounding errors
because of that **** qbasic where they thought it was a good idea to round everyones numbers
print cast(integer, 0.9) '' 1
dim as integer t = 0.9
print t '' 1
is there any way to turn this "feature" off? and why is it even there?
no one else feeling like they are coding with one eye watching for the awesome rounding feature?
(i'm really sorry, but i just can't get used to this)
my code is usually littered with int(x) because of this... it makes me teary-eyed :)
anyways, before i go out on a venting mission again, how can you get float to int with floor, without doing extra work?
what is the actual assembly for this?
edit: here are some solutions:
http://stackoverflow.com/questions/2352 ... s-to-floor
-
- Posts: 649
- Joined: Jun 09, 2005 0:08
Re: Need advice: cast(integer, float)
I have no idea if this could break anything in FB but you could change the fpu rounding mode.
Code: Select all
function get_fpu_control_word()as integer
asm fstcw [function]
end function
sub set_fpu_control_word(byval cw as integer)
asm fldcw [cw]
end sub
sub set_fpu_rounding_mode(byval mode as integer)
mode=(get_fpu_control_word() and &hf3ff)or((mode and 3)shl 10)
asm fldcw [mode]
end sub
sub main
dim as integer original_fpu_control_word=get_fpu_control_word()
'rounding mode:
'0=nearest
'1=round down
'2=round up
'3=truncate
set_fpu_rounding_mode(1)
dim as single f=1.6
dim as integer i=f
print i
'put the fpu back the way it was
set_fpu_rounding_mode(original_fpu_control_word)
end sub
main
sleep
Last edited by Stonemonkey on Dec 05, 2012 15:32, edited 1 time in total.
Re: Need advice: cast(integer, float)
There is also fix().
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Need advice: cast(integer, float)
You might find Frac() useful too. (All these operations are more consise than Cast(Integer, ...)).
For maximum conciseness, if you're really desparate, you could #define i(x) int(x)..
Personally, I think this is one of those cases where it's better to be explicit about what you want to happen.
I'm having trouble imagining why you might want to use Int() so frequently that it makes your code hard to read. What sort of code are you working on?
For maximum conciseness, if you're really desparate, you could #define i(x) int(x)..
Personally, I think this is one of those cases where it's better to be explicit about what you want to happen.
I'm having trouble imagining why you might want to use Int() so frequently that it makes your code hard to read. What sort of code are you working on?
Re: Need advice: cast(integer, float)
A related question, is there a good fast way to perform floored integer division without using the fpu? Eg. given two integers A and B, compute C = floor(A/B). Since the inputs are integers and the output is a well defined integer, it should be possible without using floating point functions like int. However the integer division operator in freebasic '\', actually rounds towards 0 like fix, and thus does not handle the negative values correctly.
I have figured out how to do this using mod twice before dividing however this is essentially using 3 divisions to compute 1 division. Is there a faster/better way?
I have figured out how to do this using mod twice before dividing however this is essentially using 3 divisions to compute 1 division. Is there a faster/better way?
Code: Select all
#Define FloorDivide(N, D) (((N)-((N)Mod(D)+(D))Mod(D))\(D))
Re: Need advice: cast(integer, float)
i'm not using int() in many places in my code, the problem is the rounding!
one of many things that make me insane: (1.99) \ 2 should return 0, but returns 1 (huge debug fest)
sometimes it matters, sometimes it matters not, but who expects their conversions to be rounded at all!
(i'm not trying to do i(x) here! shorter code isn't valuable to me)
it's only about trying to avoid having the compiler perform lots of rounding on my float to ints
Fix(x) is the equivalent of C (int)x ?
i don't think so: Note: this function is also equivalent to number - Frac(number)
so, that leaves int(x), which is equivalent to what? a function call? Bryn told me int() was slow like a turtle
that's the whole point of this thread! to avoid slow int(), since it's apparent to me that it's 2 fpu calls or 1 sse1 call to do 50% of the float to int cases
According to stonemonkey (since i don't know any ASM), FB doesn't use any extra asm to keep this rounding-circus going?
it's too bad, that it rounds by default.. i wish it didn't
one of many things that make me insane: (1.99) \ 2 should return 0, but returns 1 (huge debug fest)
sometimes it matters, sometimes it matters not, but who expects their conversions to be rounded at all!
(i'm not trying to do i(x) here! shorter code isn't valuable to me)
it's only about trying to avoid having the compiler perform lots of rounding on my float to ints
Fix(x) is the equivalent of C (int)x ?
i don't think so: Note: this function is also equivalent to number - Frac(number)
so, that leaves int(x), which is equivalent to what? a function call? Bryn told me int() was slow like a turtle
that's the whole point of this thread! to avoid slow int(), since it's apparent to me that it's 2 fpu calls or 1 sse1 call to do 50% of the float to int cases
According to stonemonkey (since i don't know any ASM), FB doesn't use any extra asm to keep this rounding-circus going?
it's too bad, that it rounds by default.. i wish it didn't
Re: Need advice: cast(integer, float)
I second that! Rounding should get banished to #LANG "qb" or completetly removed.Gonzo wrote:... the problem is the rounding!
-
- Posts: 649
- Joined: Jun 09, 2005 0:08
Re: Need advice: cast(integer, float)
All that's happening is that FB uses the FIST (or FISTP) instruction 'Floating point Integer STore (and Pop)'According to stonemonkey (since i don't know any ASM), FB doesn't use any extra asm to keep this rounding-circus going?
it's too bad, that it rounds by default.. i wish it didn't
With that instruction, the FPU has to round the number first before it's stored and that rounding depends on what rounding mode the FPU control word is set to, not anything to do with the code FB generates other than what it's already set the FPU control word to.
You can change the rounding mode like I showed though I'm not sure if that could break anything else in FB, I would hope that anything that's sensitive to the FPU rounding mode will deal with that itself but I can't say for sure.
Re: Need advice: cast(integer, float)
Thank you stonemonkey for the rounding mode sample!
Re: Need advice: cast(integer, float)
I can’t see any efficient way to do the truncation with integer code, but it’s apparently possible to do better than the FreeBASIC INT function.
Edit:
Changed the code and results to include the CRT floor function.
Running on a P3:
Running on a P4 (Northwood):
Hopefully, the more recent processors will improve on the P3 results.
Edit:
Changed the code and results to include the CRT floor function.
Code: Select all
''===================================================================================
#include "counter.bas"
#include "crt.bi"
''===================================================================================
''
'' The newer cycle count macros are available here:
''
'' http://www.freebasic.net/forum/viewtopic.php?f=7&t=20003
''
''===================================================================================
dim as double d = 12345.6789
dim as integer i
''===================================================================================
asm nop
i = int(d)
asm nop
/'
nop
fld qword ptr [ebp-12]
sub esp, 4
fnstcw [esp]
mov eax, [esp]
and eax, 0b1111001111111111
or eax, 0b0000010000000000
push eax
fldcw [esp]
add esp, 4
frndint
fldcw [esp]
add esp, 4
fistp dword ptr [ebp-16]
nop
'/
''===================================================================================
''--------------------------------------------------------------------------
'' This procedure is very nearly a direct copy of Agner Fog’s MASM code for
'' truncating a double towards zero without changing the FPU control word.
'' See optimizing_assembly.pdf available here:
''
'' http://www.agner.org/optimize/
''
''--------------------------------------------------------------------------
function truncate naked( byval x as double ) as integer
asm
fld qword ptr [esp+4] '' x
sub esp, 12 '' space for local variables
fist dword ptr [esp] '' rounded value
fst dword ptr [esp+4] '' float value
fisub dword ptr [esp] '' subtract rounded value
fstp dword ptr [esp+8] '' difference
pop eax '' rounded value
pop ecx '' float value
pop edx '' difference (float)
test ecx, ecx '' test sign of x
js short NEGATIVE
add edx, 0x7FFFFFFF '' produce carry if difference < -0
sbb eax, 0 '' subtract 1 if x-round(x) < -0
ret 8
NEGATIVE:
xor ecx, ecx
test edx, edx
setg cl '' 1 if difference > 0
add eax, ecx '' add 1 if x-round(x) > 0
ret 8
end asm
end function
''===================================================================================
i = int(d)
print i
i = floor(d)
print i
i = truncate(d)
print i
print
SetProcessAffinityMask( GetCurrentProcess(), 1)
sleep 5000
for j as integer = 1 to 4
counter_begin( 10000000, REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL )
counter_end()
print counter_cycles;" cycles, empty"
counter_begin( 10000000, REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL )
i = int(d)
counter_end()
print counter_cycles;" cycles, int()"
counter_begin( 10000000, REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL )
i = floor(d)
counter_end()
print counter_cycles;" cycles, floor()"
counter_begin( 10000000, REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL )
i = truncate(d)
counter_end()
print counter_cycles;" cycles, truncate()"
print
next
sleep
Code: Select all
12345
12345
12345
0 cycles, empty
65 cycles, int()
156 cycles, floor()
33 cycles, truncate()
0 cycles, empty
65 cycles, int()
156 cycles, floor()
33 cycles, truncate()
0 cycles, empty
65 cycles, int()
156 cycles, floor()
33 cycles, truncate()
0 cycles, empty
65 cycles, int()
156 cycles, floor()
33 cycles, truncate()
Code: Select all
12345
12345
12345
0 cycles, empty
78 cycles, int()
223 cycles, floor()
124 cycles, truncate()
0 cycles, empty
78 cycles, int()
226 cycles, floor()
124 cycles, truncate()
0 cycles, empty
78 cycles, int()
224 cycles, floor()
124 cycles, truncate()
0 cycles, empty
77 cycles, int()
224 cycles, floor()
124 cycles, truncate()
Last edited by MichaelW on Dec 06, 2012 22:21, edited 1 time in total.
Re: Need advice: cast(integer, float)
xmm (sse2):
classic (x86 pre-2004):
Code: Select all
cvttss2si 0x4(%esp), %eax ; truncation
ret
modern (x86 sse3):
sub $0x4,%esp
flds 0x8(%esp) ; can be removed if already in register
fisttpl (%esp)
mov (%esp),%eax
add $0x4,%esp
ret
Code: Select all
sub $0x8, %esp ; allocate stack space
fnstcw 0x6(%esp) ; save floating-point control word
flds $0xc(%esp) ; push floating-point param onto fp stack
movzwl 0x6(%esp), %eax ; move prev fp control word into %eax
mov $0xc, %ah ; set rounding mode of control word to "truncate"
mov %ax, 0x4(%esp) ; save it *back* to the stack
fldcw 0x4(%esp) ; set the floating-point control word to truncate
fistp 0x2(%esp) ; store integer from the fp stack to the stack
fldcw 0x6(%esp) ; set the fp control word back to what it was
movzwl 0x2(%esp), %eax ; read the value into eax (the return value)
add $0x8, %esp ; give the stack space back
ret
-
- Posts: 649
- Joined: Jun 09, 2005 0:08
Re: Need advice: cast(integer, float)
Is there any reason not to just switch the fpu rounding mode? `
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Need advice: cast(integer, float)
Once you have the quotient, the remainder can be found using a multiply and subtract. A small amount of extra logic is needed with signed division to make sure the remainder has the right sign: it has to be non-negative for positive divisors and non-positive for negative divisors. And of course the quotient has to be adjusted to ensure a = b * quotient + remaindergothon wrote:A related question, is there a good fast way to perform floored integer division without using the fpu? Eg. given two integers A and B, compute C = floor(A/B). Since the inputs are integers and the output is a well defined integer, it should be possible without using floating point functions like int. However the integer division operator in freebasic '\', actually rounds towards 0 like fix, and thus does not handle the negative values correctly.
I have figured out how to do this using mod twice before dividing however this is essentially using 3 divisions to compute 1 division. Is there a faster/better way?Code: Select all
#Define FloorDivide(N, D) (((N)-((N)Mod(D)+(D))Mod(D))\(D))
Here's some code that does it. It is hopefully easy enough to understand, and can probably be optimised.
Code: Select all
function floordivmod(byval a as integer, byval b as integer, byref r as integer = 0) as integer
if b = 0 then return 0
dim as integer q = a \ b
r = a - q*b
if (r < 0) xor (b < 0) then
if r <> 0 then
r += b
q -= 1
end if
end if
assert( q = int(a / b) )
assert( a = q * b + r )
return q
end function
dim as integer a, b, q, r
for b = -2 to 2: if b = 0 then continue for
for a = -2 to 2
q = floordivmod(a, b, r)
print a; " / "; b; " = "; q; ", r" & r
next
next
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Need advice: cast(integer, float)
By the way, how does C tend perform (int)f in x86? Does it have to adjust the rounding mode each time?