this applies to 3D vertex coordinates

if I was planing to lower the quality and byte size to a bu16, bs16, bu8, or bs8 mantissa,
how would I calculate the exponent I'd need to restore them when the data is needed??

some help understanding, here's the code that reads the data I'm trying to modify and export:

``````    def DataType(DT,exponent):
if DT==0: return bu8()/pow(2.0,exponent) # 8bit unsigned pseudo-float
if DT==1: return bs8()/pow(2.0,exponent) # 8bit signed pseudo-float
if DT==2: return bu16()/pow(2.0,exponent) # 16bit unsigned pseudo-float
if DT==3: return bs16()/pow(2.0,exponent) # 16bit signed pseudo-float
if DT==4: return bf32() #32bit float
``````

Edited by DarkPikachu

3
Contributors
15
Replies
95
Views
3 Years
Discussion Span
Last Post by DarkPikachu

If I understand your problem, the solution is to define an 'inverse function'

for each 'function' :

``````def numSquared( num ):
return num*num

def inverseNumSquared( num ):
return num**0.5
``````

Edited by David W

ok, I already know that's not gonna work well... heh
here's an example:

``````exponent = 11
``````

here's a bunch of raw bs16 vert coord values with their evaluations:

``````28C0 - 5.09375   # 0x28C0 / pow(2.0, 11 )
04C0 - 0.59375   # 0x04C0 / pow(2.0, 11 )
F500 - -1.375    # ( 0xF500 -65536) / pow(2.0, 11 )

3160 - 6.171875  # 0x3160 / pow(2.0, 11 )
0DA0 - 1.703125  # 0x0DA0 / pow(2.0, 11 )
EB80 - -2.5625   # ( 0xEB80 -65536) / pow(2.0, 11 )

2320 - 4.390625  # 0x2320 / pow(2.0, 11 )
0B80 - 1.4375    # 0x0B80 / pow(2.0, 11 )
EF60 - -2.078125 # ( 0xEF60 -65536) / pow(2.0, 11 )
``````

so... now that I have all of those floats, how do I get `11` from them??

Edited by DarkPikachu

figured out something (no code yet)

just run a loop multiplying all floats by 2 until everything is at n.0
the exponent is the number of runs the loop takes.

EDIT:

``````>>> pow(2.0,11)
2048.0

>>> 0x28C0
10432

>>> 10432 / 2048.0
5.09375

>>> 5.09375 * 2048.0
10432.0

>>> 5.09375 * 2*2*2*2*2*2*2*2*2*2*2 # 11 times
10432.0

>>> 5.09375 *2*2*2*2*2 # 5 times
163.0

>>> 5.09375 *2*2*2*2 # 4 times does this:
81.5
``````

EDIT2:
here's my current tests:

``````>>> def getexp(FL):
exponent = 1
while True:
SE = pow(2.0,exponent)
for F in FL:
SF = F*SE
if SF-int(SF)!= 0.0: addexp = True; break
break
return exponent

>>> getexp([
5.09375,
0.59375,
-1.375,
6.171875,
1.703125,
-2.5625,
4.390625,
1.4375,
-2.078125
])

6
``````

I'm not expecting 11 from these floats, however, all floats should be divided by the returned exponent.

Edited by DarkPikachu

also, how could I lower the quality of a float who's mantissa is bigger than 16 bits??

basically I could say I want to "round" the mantissa to 16 bits

EDIT:
if I had to take a guess...
just calculate the exponent until any float in the list is bigger than 65535.
if it's bigger, just subtract from the exponent and int(float_value)

EDIT2:
expanding on that guess, from the code above:

``````if int(SF)>65535: return exponent-1
``````

and just run each float through

``````int( value * pow(2.0, exponent) )
``````

Edited by DarkPikachu

also, how could I lower the quality of a float who's mantissa is bigger than 16 bits

A normal float is written on 64 bits, with 1 sign bit, 11 exponent bits and 52 mantissa bits. Why don't you convert it to a shorter ieee format ? It seems that you are always trying to solve the same issues. Use my anyfloat snippet ! Here is how to round pi from format `(11, 52)` to `(3, 12)` for example

``````>>> from anyfloat import anyfloat
>>> import math
>>> x = anyfloat.from_float(math.pi)
>>> y = x.to_ieee((3, 12))
>>> z = anyfloat.from_ieee(y, (3,12))
>>> float(z)
3.1416015625
``````

Edit: notice that I wrote the anyfloat snippet three years ago because of one of your threads about ieee 754 format.

Edited by Gribouillis

because this is a Nintendo optimized vector format,
the vertices, normals (bi-normals and tangents), and UVs can either be int (signed or unsigned 8 or 16 bit) or float (32bit only).
if they're int, the vertex/facepoint attributes specify an exponent which is applied to all vectors in the category.

I can read the int verts just fine, I'm trying to write them, so I need the common exponent with a bunch of ints.

Edited by DarkPikachu

Here is how to round pi from format `(11, 52)` to `(3, 12)` for example

btw, I feel rounding like that would screw up the result...
I'mma try a few tests on this

EDIT:
here's the idea:

``````>>> 5.09375 * pow(2.0, 4)
81.5
>>> 81 / pow(2.0, 4)
5.0625
>>> 1.703125 * pow(2.0, 5)
54.5
>>> 54 / pow(2.0, 5)
1.6875
``````

Edited by DarkPikachu

I dont understand you examples. If you think the anyfloat rounding will screw up the result, you can keep the same exponent, I mean shorten only the mantissa

``````from anyfloat import anyfloat

def round_mantissa(x, bits=16):
a = anyfloat.from_float(x)
x = a.to_ieee((11, bits))
return float(anyfloat.from_ieee(x, (11, bits)))

if __name__ == '__main__':
x = 5.87374776266
y = round_mantissa(x)
print(repr(y))

""" my output -->
5.87371826171875
"""
``````

The rounding algorithm is very good in the anyfloat module. For example

``````round_mantissa(1.703125, bits=4)
``````

gives 1.6875

Edited by Gribouillis

I dont understand you examples

in this:

``````>>> 5.09375 * pow(2.0, 4)
81.5
``````

`5.09375` is the float you want to write to the file
`4` is the common exponent to resolve a 16bit mantissa to an 8bit mantissa (will be written to the file)
int( `81.5` ) is what will be written to the file as the 8bit vector

and also the counter-example:

``````>>> 81 / pow(2.0, 4)
5.0625
``````

`81` is what's read from the file
`4` is the common exponent (read from the file)
`5.0625` is the result to supply

EDIT:
just tried `4` on my second example:

``````>>> 1.703125 * pow(2.0, 4)
27.25
>>> 27 / pow(2.0, 4)
1.6875
>>>
``````

that's very interesting:

``````>>> 54 / pow(2.0, 5)
1.6875
``````

Edited by DarkPikachu

ok, so here's what I've got so far:

``````floats = [
5.09375,
0.59375,
-1.375,
6.171875,
1.703125,
-2.5625,
4.390625,
1.4375,
-2.078125
]

def getexp(floats,depth=16):
exponent = 1
D = (1<<depth)-1
while True:
SE = pow(2.0,exponent)
for F in floats:
SF = abs(F)*SE
if int(SF)>D: return exponent-1
if SF-int(SF)!= 0.0: addexp = True; break
break
return exponent

depth=8
exponent = getexp(floats,depth)

print 'int_depth =', depth,'bit'
print 'exponent =', exponent
print

print 'given \t\t> returned \t> result\n'

evaluator = pow(2.0, exponent )

for f in floats:
floating_int = int(f*evaluator)
print f,' \t>',floating_int,' \t\t>',floating_int/evaluator
``````

not quite finished...
still have to deal with the sign yet...
but here's the current test results:

``````>>>
int_depth = 8 bit
exponent = 5

given       > returned  > result

5.09375     > 163       > 5.09375
0.59375     > 19        > 0.59375
-1.375      > -44       > -1.375
6.171875    > 197       > 6.15625
1.703125    > 54        > 1.6875
-2.5625     > -82       > -2.5625
4.390625    > 140       > 4.375
1.4375      > 46        > 1.4375
-2.078125   > -66       > -2.0625
>>>  ================================ RESTART ================================
>>>
int_depth = 16 bit
exponent = 6 # 11 should fit here, 6 is just the max for these floats

given       > returned  > result

5.09375     > 326       > 5.09375
0.59375     > 38        > 0.59375
-1.375      > -88       > -1.375
6.171875    > 395       > 6.171875
1.703125    > 109       > 1.703125
-2.5625     > -164      > -2.5625
4.390625    > 281       > 4.390625
1.4375      > 92        > 1.4375
-2.078125   > -133      > -2.078125
>>>
``````

I don't think the precision could be any better... heh
take note the `int_depth` applies to the range of the `returned` values (ignore the signed values for now)

Here is another way, using the machine representation of floating numbers

``````#!/usr/bin/env python
# -*-coding: utf8-*-
'''computes power 2 valuation in machine floats
'''
from __future__ import (absolute_import, division,
print_function, unicode_literals)

pow52 = 2**52

def expo(f):
n = int(float(f) * pow52)
return 53 - (n & -n).bit_length() if n else 0

if __name__ == '__main__':
floats = [
5.09375, 0.59375, -1.375, 6.171875, 1.703125,
-2.5625, 4.390625, 1.4375, -2.078125, 0.0
]
for f in floats:
n = expo(f)
print("{:<10} {:<4} {}".format(f, n, f * 2**n))

""" my output -->
5.09375    5    163.0
0.59375    5    19.0
-1.375     3    -11.0
6.171875   6    395.0
1.703125   6    109.0
-2.5625    4    -41.0
4.390625   6    281.0
1.4375     4    23.0
-2.078125  6    -133.0
0.0        0    0.0
"""
``````

Edit: it may not work for too large or too small floats, but there are ways to improve it.

Edited by Gribouillis

where's your common exponent to evaluate all those "ints" (the n.0 floats) by??
also, what's the bit-depth of those ints, 8bit or 16bit??

as posted earlier, this is the provided data for some of the vertices in a model file:

``````28C0 04C0 F500 3160 0DA0 EB80 2320 0B80 EF60
``````

the data format is bs16, and the provided exponent is `11` (the data format of the exponent is usually bu8, however MDL0 files use bu32)

when evaluated, the results are the floats you see in my examples:

``````5.09375
0.59375
-1.375
6.171875
1.703125
-2.5625
4.390625
1.4375
-2.078125
``````

the reverse-evaluation I'm using (multiplying the value instead of dividing it) returns a common exponent of `6` for those floats when using bu16 format, the common exponent for bu8 is `5` as shown in my examples.

a flaw with my evaluator though is it doesn't process signed floats correctly...
any idea of how I could deal with these??

if I had to guess, use a pre-evaluator on all floats pushing them into positive bounds before finding the exponent. >_>

EDIT:
or I could just ignore `u`/`s` and automate it >_>
most of the time it should result in `s`

EDIT2:
nvm, maybe it is handling the signs correctly:

``````>>> for n in [0x28C0, 0x04C0, 0xF500-65536, 0x3160, 0x0DA0, 0xEB80-65536, 0x2320, 0x0B80, 0xEF60-65536]: print n

10432
1216
-2816
12640
3488
-5248
8992
2944
-4256
``````

I was major-derping there... heh

Edited by DarkPikachu

where's your common exponent to evaluate all those "ints" (the n.0 floats) by??

Well my idea is to take the smallest exponent which yields an int for each of the floats, then take the largest of these exponents to get the common exponent. This gives 6.

also, what's the bit-depth of those ints, 8bit or 16bit??

I don't understand this question, or may be I don't understand the problem very well.

ok I see, I was focusing on the wrong tangent, interesting approach there, I like it >.>

the bit-depth applies to the vectors themselves:

``````28C0 04C0 F500 3160 0DA0 EB80 2320 0B80 EF60
``````

^ these are the mantissas for each value, the format here being bs16

thr real challenge though, they'll fit in 16bit depth because they come from 16bit depth...
but now try fitting them in 8bit depth. ;)

EDIT:
re-reading yours, mine tests if the result is too big:

``````if int(SF)>D+(F<0): return exponent-1
``````

mine stops the loops short if anything's wrong, so it's not like it's running through the entire thing every time ;)

EDIT2:
modified mine for signed ranges: (-128 to 127 and such)

``````def getexp(floats,depth=16):
signed = False
for f in float:
if f<0: signed = True; break
exponent = 1
D = ((1<<depth)-1)/(1+signed)
while True:
SE = pow(2.0,exponent)
for F in floats:
SF = abs(F)*SE
if int(SF)>D+(F<0): return exponent-1
if SF-int(SF)!= 0.0: addexp = True; break
break
return exponent
``````

^if any float is signed, it will auto-assume signed 16bit format

Edited by DarkPikachu

correction in that code:

``````for f in floats:
``````
This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.