General FreeBASIC programming questions.
Gonzo
Posts: 722
Joined: Dec 11, 2005 22:46

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
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14

### Re: Need advice: cast(integer, float)

Int() should give you the floored value, CInt() should give you the rounded value.
Stonemonkey
Posts: 587
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 functionsub set_fpu_control_word(byval cw as integer)    asm fldcw [cw]end subsub 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 subsub 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 submainsleep`
Last edited by Stonemonkey on Dec 05, 2012 15:32, edited 1 time in total.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

### Re: Need advice: cast(integer, float)

There is also fix().
counting_pine
Posts: 6174
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?
gothon
Posts: 224
Joined: Apr 11, 2011 22:22

### 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?

Code: Select all

`#Define FloorDivide(N, D) (((N)-((N)Mod(D)+(D))Mod(D))\(D))`
Gonzo
Posts: 722
Joined: Dec 11, 2005 22:46

### 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
TJF
Posts: 3486
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

### Re: Need advice: cast(integer, float)

Gonzo wrote:... the problem is the rounding!

I second that! Rounding should get banished to #LANG "qb" or completetly removed.
Stonemonkey
Posts: 587
Joined: Jun 09, 2005 0:08

### Re: Need advice: cast(integer, float)

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

All that's happening is that FB uses the FIST (or FISTP) instruction 'Floating point Integer STore (and Pop)'

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.
dafhi
Posts: 1276
Joined: Jun 04, 2005 9:51

### Re: Need advice: cast(integer, float)

Thank you stonemonkey for the rounding mode sample!
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

### 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.

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.6789dim as integer i''===================================================================================asm nopi = 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 asmend function''===================================================================================i = int(d)print ii = floor(d)print ii = truncate(d)print iprintSetProcessAffinityMask( GetCurrentProcess(), 1)sleep 5000for 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()"    printnextsleep`

Running on a P3:

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()`

Running on a P4 (Northwood):

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()`

Hopefully, the more recent processors will improve on the P3 results.
Last edited by MichaelW on Dec 06, 2012 22:21, edited 1 time in total.
Gonzo
Posts: 722
Joined: Dec 11, 2005 22:46

### Re: Need advice: cast(integer, float)

xmm (sse2):

Code: Select all

`cvttss2si  0x4(%esp), %eax    ; truncationretmodern (x86 sse3):sub     \$0x4,%espflds    0x8(%esp)           ; can be removed if already in registerfisttpl (%esp)mov     (%esp),%eaxadd     \$0x4,%espret`

classic (x86 pre-2004):

Code: Select all

`sub      \$0x8, %esp       ; allocate stack spacefnstcw   0x6(%esp)        ; save floating-point control wordflds     \$0xc(%esp)       ; push floating-point param onto fp stackmovzwl   0x6(%esp), %eax  ; move prev fp control word into %eaxmov      \$0xc, %ah        ; set rounding mode of control word to "truncate"mov      %ax, 0x4(%esp)   ; save it *back* to the stackfldcw    0x4(%esp)        ; set the floating-point control word to truncatefistp    0x2(%esp)        ; store integer from the fp stack to the stackfldcw    0x6(%esp)        ; set the fp control word back to what it wasmovzwl   0x2(%esp), %eax  ; read the value into eax (the return value)add      \$0x8, %esp       ; give the stack space backret`
Stonemonkey
Posts: 587
Joined: Jun 09, 2005 0:08

### Re: Need advice: cast(integer, float)

Is there any reason not to just switch the fpu rounding mode? `
counting_pine
Posts: 6174
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

### Re: Need advice: cast(integer, float)

gothon 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))`

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 + remainder
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 functiondim as integer a, b, q, rfor 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    nextnext`
counting_pine