Note
Go to the end to download the full example code
Float Conversion#
I came up with the following question ? What is the probability this holds?
Probability (float64)x == (float32)x#
Let’s evaluate how many time we draw a random double number equal to its float conversion.
import random
import numpy
import pandas
import matplotlib.pyplot as plt
rnd = numpy.random.random(100000000)
rnd.shape, rnd.dtype
((100000000,), dtype('float64'))
rnd32 = rnd.astype(numpy.float32).astype(numpy.float64)
equal = (rnd == rnd32).sum()
equal
2
It is very low. Let’s check the reverse is true.
rnd32b = rnd32.astype(numpy.float64).astype(numpy.float32)
equal = (rnd32b == rnd32).sum()
equal
100000000
Let’s study the distribution of the difference.
(-2.980232227667301e-08, 2.9802322387695312e-08)
(1.1707908198488326e-08, 0.999999997344628)
(array([ 49963., 50274., 49885., 50054., 49733., 50129., 50129.,
49661., 50152., 49916., 49935., 49647., 50099., 50316.,
50319., 49935., 49975., 49752., 49886., 50382., 50160.,
50238., 50184., 50064., 49792., 49787., 49753., 49994.,
49931., 49798., 49841., 50086., 50181., 49978., 50457.,
50287., 49766., 50253., 50568., 49901., 49840., 49741.,
49857., 49760., 50226., 50138., 50004., 49938., 50228.,
49994., 50084., 49850., 50082., 49734., 50238., 50173.,
49887., 49621., 49718., 49805., 50141., 49961., 50501.,
49761., 50147., 50197., 49898., 50178., 50288., 50483.,
49879., 49811., 49906., 50340., 49742., 50052., 49591.,
49824., 49983., 49990., 50020., 50228., 50390., 49613.,
50192., 50138., 50216., 49993., 50059., 49688., 50088.,
49866., 49953., 49624., 50292., 50012., 49604., 49483.,
50285., 49823., 50489., 49709., 49885., 49928., 49746.,
49798., 50093., 49853., 49730., 49813., 50102., 50302.,
49748., 50388., 50152., 50393., 49906., 49655., 49947.,
49643., 49817., 49908., 49918., 49683., 49782., 50497.,
50119., 49820., 49987., 49766., 50076., 49816., 49657.,
49921., 49881., 49648., 49986., 49888., 49905., 50332.,
50105., 50166., 50245., 49731., 50165., 49809., 50033.,
49802., 50022., 49839., 49826., 50247., 50104., 49592.,
50273., 50137., 50227., 49907., 50202., 49952., 49914.,
50235., 50083., 50450., 49899., 50036., 49758., 49950.,
49753., 49996., 49790., 50414., 50297., 50022., 49751.,
49480., 49489., 49785., 49874., 49945., 50043., 50145.,
49908., 49877., 50278., 49836., 49805., 50107., 50129.,
49787., 50059., 49948., 49850., 49279., 49904., 50147.,
50017., 50133., 49863., 50190., 50115., 49939., 50015.,
49774., 50035., 50379., 49921., 50003., 50016., 49788.,
50202., 50087., 49708., 50130., 49760., 50260., 49805.,
49681., 49938., 49662., 50208., 49817., 49763., 50024.,
49744., 49904., 50044., 49674., 49949., 50261., 50042.,
50134., 49911., 50275., 49408., 50177., 50261., 49746.,
49783., 49968., 50222., 49849., 50005., 50396., 50019.,
49992., 50005., 49840., 49871., 50161., 99841., 99472.,
99613., 99570., 100289., 100046., 100464., 100077., 99902.,
100140., 99892., 99336., 100654., 100032., 99882., 100233.,
99706., 99981., 100122., 100066., 100084., 100226., 99957.,
100230., 99782., 99795., 99914., 100052., 99722., 99873.,
99665., 100297., 100168., 100247., 100509., 99821., 100015.,
99970., 100271., 100113., 99704., 99924., 99497., 99879.,
100078., 100590., 100155., 99892., 100030., 100112., 99917.,
99124., 99846., 99879., 100344., 100118., 99798., 100236.,
100274., 100051., 100196., 100043., 100137., 99984., 99826.,
99716., 99660., 99822., 100393., 99941., 99862., 100641.,
100233., 100095., 100088., 99847., 99798., 99198., 99916.,
99414., 100297., 100164., 99949., 100233., 100409., 99758.,
99420., 99787., 99788., 100121., 99967., 100436., 99526.,
99854., 99776., 100049., 100552., 100275., 100355., 99847.,
100448., 99767., 99811., 100036., 99458., 100199., 99557.,
100008., 99978., 100061., 99920., 100161., 100173., 99704.,
99995., 99802., 100134., 99884., 100519., 100114., 100338.,
100384., 100487., 100295., 100353., 149916., 149310., 150333.,
150416., 149839., 149529., 149478., 149822., 150292., 149622.,
150233., 149203., 149940., 150204., 149900., 149963., 150287.,
150257., 150414., 150056., 149964., 151025., 149675., 149890.,
151035., 150350., 149381., 149941., 150470., 150331., 149950.,
149953., 150112., 149990., 150477., 150156., 150109., 149977.,
150421., 149519., 151008., 149814., 149479., 149595., 150369.,
149581., 150004., 149167., 150247., 150415., 150249., 149402.,
149923., 149681., 150040., 149639., 149461., 149496., 150013.,
149688., 150318., 150323., 174958., 200380., 200487., 199979.,
199974., 200539., 199565., 199848., 199602., 199515., 200357.,
199935., 200333., 199853., 200144., 200925., 199464., 199975.,
200546., 200259., 200033., 199956., 200055., 199891., 199670.,
200423., 199857., 200214., 199431., 199938., 199933., 212276.,
250392., 249715., 249747., 249827., 250570., 250006., 249926.,
250680., 250132., 250351., 250300., 250327., 250551., 249389.,
250261., 280722., 300724., 301269., 300319., 299673., 300174.,
300393., 300941., 340075., 349606., 350175., 351367., 394785.,
399463., 446456., 547868., 547248., 447345., 400413., 394336.,
350453., 350374., 349853., 340744., 299696., 299264., 299623.,
299284., 300180., 299422., 299690., 281928., 249661., 250987.,
249624., 249525., 248930., 251016., 250459., 250537., 249421.,
249530., 249440., 249875., 249011., 249793., 250569., 212539.,
200256., 200108., 199669., 199862., 200887., 199813., 200385.,
200691., 200857., 200403., 200283., 199814., 200289., 200272.,
200454., 200592., 199948., 199647., 199503., 200097., 200132.,
199746., 199954., 199949., 199618., 200507., 200126., 199233.,
200287., 199954., 175179., 150726., 149757., 150029., 150176.,
149998., 150113., 149526., 149565., 149543., 149889., 149088.,
149663., 149666., 150393., 149490., 150285., 150222., 150432.,
149297., 150460., 150416., 149597., 150348., 149642., 150655.,
149900., 149625., 150082., 149435., 149608., 149885., 149998.,
150333., 149973., 150121., 150509., 149865., 150074., 150354.,
148577., 149716., 149784., 149172., 150505., 149303., 150089.,
150644., 150181., 149798., 149730., 150859., 149222., 150063.,
151217., 150247., 150147., 149463., 150120., 150013., 150357.,
149538., 150067., 99795., 100091., 99883., 99982., 100185.,
100085., 99848., 100011., 99997., 99872., 99794., 100504.,
99795., 100183., 100429., 99615., 99806., 100723., 100085.,
99747., 99803., 100030., 99654., 99971., 99572., 100242.,
99756., 100155., 99531., 100722., 99663., 100108., 100049.,
99875., 100462., 99437., 99806., 99581., 100076., 99875.,
100186., 100307., 100105., 100386., 99829., 99786., 99972.,
99824., 99875., 99928., 99880., 99724., 99941., 99721.,
99739., 99821., 100647., 99951., 99792., 99698., 99975.,
99938., 99982., 100140., 99968., 99515., 100209., 99309.,
100301., 100103., 99882., 100322., 100108., 99192., 100235.,
100017., 99721., 99752., 100436., 99516., 99919., 100142.,
100046., 99732., 100298., 99714., 100146., 100457., 100696.,
100055., 100280., 100281., 99773., 99778., 99540., 100063.,
99904., 100059., 100266., 99951., 100221., 99978., 99878.,
99400., 100034., 100087., 99972., 100174., 100088., 100219.,
99889., 99893., 99386., 99690., 99665., 100326., 100277.,
100292., 99751., 100168., 99994., 99798., 100166., 99851.,
99774., 50137., 50030., 49828., 50470., 49841., 49443.,
50134., 49982., 50072., 49996., 50144., 49811., 50031.,
49959., 50323., 49980., 50105., 50461., 49985., 49985.,
49872., 49933., 50142., 49749., 49580., 50557., 50000.,
50610., 50520., 49885., 50052., 49871., 49816., 50295.,
49949., 49904., 49884., 49977., 49683., 49708., 50317.,
49928., 50221., 49889., 49543., 49727., 50151., 50036.,
50334., 50175., 50195., 50283., 49604., 50174., 49802.,
50026., 49901., 50154., 49849., 49909., 49901., 50026.,
50127., 49689., 49846., 50121., 50226., 50052., 49808.,
49990., 50220., 49868., 49654., 50366., 50031., 49733.,
50075., 50249., 49961., 50086., 50082., 49649., 49867.,
50177., 49737., 50365., 50071., 49927., 49978., 50030.,
50320., 50026., 50033., 49960., 49748., 49936., 50247.,
49874., 49874., 50112., 49780., 50464., 49912., 50093.,
50179., 50127., 49786., 50417., 50055., 49747., 50115.,
49728., 50298., 49798., 50309., 50013., 49994., 49142.,
49869., 49954., 50056., 49913., 50358., 49984., 49997.,
50187., 49762., 50078., 49994., 50184., 50196., 50015.,
50055., 50156., 49832., 50362., 49748., 49913., 49960.,
49653., 50215., 50100., 50243., 49859., 50531., 49872.,
49935., 49923., 50029., 50236., 50228., 49846., 50275.,
49796., 50112., 50574., 49842., 50040., 49921., 49746.,
50188., 49951., 50127., 50119., 49969., 49866., 50145.,
49509., 50011., 49649., 50354., 50220., 50109., 50242.,
50096., 49849., 49649., 50086., 49863., 50196., 50014.,
50177., 49932., 50350., 49979., 50255., 49838., 49891.,
50185., 50242., 50209., 49747., 50342., 49903., 49981.,
50138., 49839., 49970., 50096., 50200., 50456., 50094.,
49865., 50249., 50356., 49937., 50169., 50444., 50173.,
50308., 50257., 49981., 50033., 49781., 50079., 50047.,
49641., 49827., 50200., 49515., 50209., 50180., 49897.,
49626., 50138., 49704., 50222., 49685., 49997., 50093.,
49989., 50275., 49839., 50154., 50158., 49904., 49797.,
50131., 49742., 50142., 50181., 50117., 50066., 49936.,
50296., 49987., 50448., 50043., 50086., 50279.]), array([-2.98023223e-08, -2.97427176e-08, -2.96831130e-08, ...,
2.96831131e-08, 2.97427177e-08, 2.98023224e-08]), <BarContainer object of 1000 artists>)
We finally check that double operations between float numpers remain floats.
for i in range(0, 100000):
i, j = random.randint(0, len(rnd32) - 1), random.randint(0, len(rnd32) - 1)
d32 = numpy.float64(rnd32[i] * rnd32[j])
d64 = numpy.float64(rnd32[i]) * numpy.float64(rnd32[j])
if d32 != d64:
raise Exception(
"Issue with somme={0} = {1} + {2}".format(
rnd32[i] + rnd32[j], rnd32[i], rnd32[j]
)
)
Interval length distribution#
Let’s imagine now we want to define an intervalle in which a double is converted to the same float. Let’s find out about it length.
def find_interval(x):
dx = numpy.abs(x - numpy.float32(x)) # usually not zero
dx /= 100
f = numpy.float32(x)
x1 = x
while numpy.float32(x1) == f:
x1 -= dx
x2 = x
while numpy.float32(x2) == f:
x2 += dx
return x1 + dx, x2 - dx
length = numpy.zeros((2000,))
for i in range(length.shape[0]):
x = rnd[i]
x1, x2 = find_interval(x)
length[i] = x2 - x1
min(length), max(length)
(3.6340828485576307e-12, 5.9604587931971764e-08)
(array([ 37., 35., 0., 57., 0., 0., 103., 0., 0.,
0., 0., 0., 239., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 490., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 1039.]), array([3.63408285e-12, 1.19565316e-09, 2.38767224e-09, 3.57969131e-09,
4.77171039e-09, 5.96372947e-09, 7.15574854e-09, 8.34776762e-09,
9.53978670e-09, 1.07318058e-08, 1.19238249e-08, 1.31158439e-08,
1.43078630e-08, 1.54998821e-08, 1.66919012e-08, 1.78839202e-08,
1.90759393e-08, 2.02679584e-08, 2.14599775e-08, 2.26519965e-08,
2.38440156e-08, 2.50360347e-08, 2.62280538e-08, 2.74200729e-08,
2.86120919e-08, 2.98041110e-08, 3.09961301e-08, 3.21881492e-08,
3.33801682e-08, 3.45721873e-08, 3.57642064e-08, 3.69562255e-08,
3.81482445e-08, 3.93402636e-08, 4.05322827e-08, 4.17243018e-08,
4.29163209e-08, 4.41083399e-08, 4.53003590e-08, 4.64923781e-08,
4.76843972e-08, 4.88764162e-08, 5.00684353e-08, 5.12604544e-08,
5.24524735e-08, 5.36444925e-08, 5.48365116e-08, 5.60285307e-08,
5.72205498e-08, 5.84125689e-08, 5.96045879e-08]), <BarContainer object of 50 artists>)
So we can approximate this interval by something like this:
ql = numpy.sort(length)[int(length.shape[0] * 0.8)]
ql
5.952199622072385e-08
An answer to the initial question#
Let’s estimate ?
def inf_strict(x, y):
f1 = x < y
f2 = numpy.float32(x) < numpy.float32(y)
return f1, f2
def count_events(fct):
rows = []
for di in range(1, 1001):
d = di * ql / 100
total = 0
ok = 0
rnd = numpy.random.random((2000 * 3,))
for i in range(0, rnd.shape[0], 3):
s = -1 if rnd[i + 2] < 0.5 else 1
x, y = rnd[i], rnd[i] + rnd[i + 1] * d * s
f1, f2 = fct(x, y)
if f1:
total += 1
if f2:
ok += 1
if (di + 10) % 100 == 0:
print(di, d, ":", ok, total)
rows.append(dict(d=d, ratio=ok * 1.0 / total, total=total))
return pandas.DataFrame(rows)
df = count_events(inf_strict)
df.head()
90 5.3569796598651465e-08 : 661 1026
190 1.1309179281937531e-07 : 837 993
290 1.7261378904009916e-07 : 889 1007
390 2.32135785260823e-07 : 923 1016
490 2.9165778148154686e-07 : 942 991
590 3.511797777022707e-07 : 938 987
690 4.1070177392299456e-07 : 929 980
790 4.702237701437184e-07 : 957 999
890 5.297457663644423e-07 : 979 1013
990 5.892677625851661e-07 : 990 1025
<Axes: xlabel='d'>
<Axes: xlabel='d'>
An answer to a similar question: what about not strict comparison?#
Let’s estimate ?
def inf_equal(x, y):
f1 = x <= y
f2 = numpy.float32(x) <= numpy.float32(y)
return f1, f2
df2 = count_events(inf_equal)
df2.head()
90 5.3569796598651465e-08 : 988 988
190 1.1309179281937531e-07 : 1006 1006
290 1.7261378904009916e-07 : 1012 1012
390 2.32135785260823e-07 : 954 954
490 2.9165778148154686e-07 : 1016 1016
590 3.511797777022707e-07 : 1011 1011
690 4.1070177392299456e-07 : 995 995
790 4.702237701437184e-07 : 1042 1042
890 5.297457663644423e-07 : 1020 1020
990 5.892677625851661e-07 : 994 994
<Axes: xlabel='d'>
def sup_strict(x, y):
f1 = x > y
f2 = numpy.float32(x) > numpy.float32(y)
return f1, f2
df3 = count_events(sup_strict)
df3.head()
90 5.3569796598651465e-08 : 660 998
190 1.1309179281937531e-07 : 789 979
290 1.7261378904009916e-07 : 870 980
390 2.32135785260823e-07 : 925 1011
490 2.9165778148154686e-07 : 895 977
590 3.511797777022707e-07 : 988 1036
690 4.1070177392299456e-07 : 952 999
790 4.702237701437184e-07 : 963 1014
890 5.297457663644423e-07 : 957 993
990 5.892677625851661e-07 : 1002 1034
<Axes: xlabel='d'>
def sup_equal(x, y):
f1 = x >= y
f2 = numpy.float32(x) >= numpy.float32(y)
return f1, f2
df4 = count_events(sup_equal)
df4.head()
90 5.3569796598651465e-08 : 1052 1052
190 1.1309179281937531e-07 : 1029 1029
290 1.7261378904009916e-07 : 971 971
390 2.32135785260823e-07 : 988 988
490 2.9165778148154686e-07 : 998 998
590 3.511797777022707e-07 : 977 977
690 4.1070177392299456e-07 : 1006 1006
790 4.702237701437184e-07 : 1031 1031
890 5.297457663644423e-07 : 976 976
990 5.892677625851661e-07 : 984 984
<Axes: xlabel='d'>
def inf_strict_neg(x, y):
f1 = (-x) >= (-y)
f2 = (-numpy.float32(x)) >= (-numpy.float32(y))
return f1, f2
dfn = count_events(inf_strict_neg)
dfn.head()
90 5.3569796598651465e-08 : 982 982
190 1.1309179281937531e-07 : 1006 1006
290 1.7261378904009916e-07 : 970 970
390 2.32135785260823e-07 : 979 979
490 2.9165778148154686e-07 : 988 988
590 3.511797777022707e-07 : 976 976
690 4.1070177392299456e-07 : 1011 1011
790 4.702237701437184e-07 : 953 953
890 5.297457663644423e-07 : 1004 1004
990 5.892677625851661e-07 : 989 989
<Axes: xlabel='d'>
Conclusion#
The result is expected. As soon as two float are rounded to the same value,
the strict inequality no longer holds. However, if you need to write a
code which has to handle double and float (in a template for example),
you should use not strict inequalities. It is easier to compare the results
but you should read some article like Is < faster than <=?.
According to
Processing costs of non-strict versus strict comparison, <
is 5-10% faster than <=
.
Total running time of the script: ( 0 minutes 44.502 seconds)