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

Recommended Answers

All 15 Replies

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

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

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
    addexp = False
    while True:
        SE = pow(2.0,exponent)
        for F in FL:
            SF = F*SE
            if SF-int(SF)!= 0.0: addexp = True; break
        if addexp: addexp = False; exponent+=1; continue
        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.

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

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.

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.

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

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

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

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
    addexp = False
    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
        if addexp: addexp = False; exponent+=1; continue
        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.

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

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

in your example's results, the bit-depth applies to your 3rd column.

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
    addexp = False
    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
        if addexp: addexp = False; exponent+=1; continue
        break
    return exponent

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

correction in that code:

for f in floats:
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.