Raymond Hettinger Profile picture
Aug 31, 2020 6 tweets 2 min read Read on X
Another building block for a #Python floating point ninja toolset:

def veltkamp_split(x):
'Exact split into two 26-bit precision components'
t = x * 134217729.0
hi = t - (t - x)
lo = x - hi
return hi, lo

csclub.uwaterloo.ca/~pbarfuss/dekk…
Input: one signed 53-bit precision float

Output: two signed 26-bit precision floats

Invariant: x == hi + lo

Constant: 134217729.0 == 2.0 ** 27 + 1.0
Example:

>>> hi, lo = veltkamp_split(pi)
>>> hi + lo == pi
True
>>> hi.hex()
'0x1.921fb58000000p+1'
>>> lo.hex()
'-0x1.dde9740000000p-26'

Note all the trailing zeros and the difference between the two exponents. Also both the lo and hi values are signed.
Use case: The 26-bit precision components can be multiplied losslessly (without rounding):

# Four exact components of e * pi:
>>> pi_hi*e_hi
8.539734226211163
>>> pi_hi*e_lo
7.207993525551209e-08
>>> pi_lo*e_hi
-7.561753118743836e-08
>>> pi_lo*e_lo
-6.382525038592985e-16
The payoff for Veltkamp-Dekker splitting and piecewise multiplication is that you can build quad precision arithmetic out of everyday double precision arithmetic.
The part that is pure magic: How do you split a 53 bit number into two 26 bit numbers? Where did the extra bit go?

Answer: The extra bit is stored in the sign bit of the "lo" component.

53 bits + 1 sign = 26 bits + 1 sign + 26 bits + 1 sign

All bits accounted for.🧐

Cool!😏

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Raymond Hettinger

Raymond Hettinger Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @raymondh

Jul 13, 2022
#Python tip: Create variable width fields in f-strings with an inner pair of curly braces.

>>> s = 'hello'
>>> n = 10
>>> f'{s:^{n}}'
' hello '

>>> n = 20
>>> f'{s:^{n}}'
' hello '

1/
Interestingly, this works for any part of the format specifier, including justification:

>>> s = 'aloha'
>>> just = '<'
>>> f'{s:{just}10}'
'aloha '

>>> just = '>'
>>> f'{s:{just}10}'
' aloha'

2/
Even the type field can also be variable:

>>> n = 123
>>> t = 'd'; f'{n:{t}}' # decimal
'123'
>>> t = 'o'; f'{n:{t}}' # octal
'173'
>>> t = 'x'; f'{n:{t}}' # hex
'7b'

3/
Read 5 tweets
Jun 12, 2022
Structural pattern matching in #Python supports float and complex literals in case statements.

However, exact equality tests for float/complex are often a bad idea.

Here's a fix.

match approximately(1.1 + 2.2):
case 3.3:
print('hit!')

1/
Without the approximately() wrapper, the case would not match due to round-off error.

>>> 1.1 + 2.2 == 3.3
False

>>> 1.1 + 2.2
3.3000000000000003

>>> 1.1 + 2.2 - 3.3
4.440892098500626e-16

So, we need an approximate match wrapper.

/2
class approximately(complex):
def __new__(cls, x, /, **kwargs):
result = complex.__new__(cls, x)
result.kwargs = kwargs
return result
def __eq__(self, other):
return cmath.isclose(self, other, **self.kwargs)

3/
Read 5 tweets
Jun 5, 2022
Here's a PDF for my #Python #PyConIT2022 talk: Structural Pattern Matching in the Real World: New tooling, real code, problems solved.

This is intermediate and advanced level Structural Pattern Matching.

tl;dr The “good stuff” is in section 1.2

dropbox.com/s/w1bs8ckekki9…
You'll find #Python pattern matching recipes for:

* Replacing literals with variables and named constants
* Replacing literals with regexes
* Replacing literals with function calls
* Replacing literals with set membership tests
We draw key learning points from real #Python applications:

* Datetime formatting mini language
* Language tokenizer
* SQL logic for JSON lines
* SQL logic for two level JSON
* Traversing arbitrary depth JSON trees
* Marshal style data serializer
Read 8 tweets
Mar 11, 2022
#Python tip: The default_factory feature of a defaultdict is only useful when building up a dictionary (automatically adding missing keys).

However, that feature is a menace when doing lookups (risking accidental dict mutation).

Consider converting back to a regular dict.

1/
# During build-up, we want the factory magic.
d = defaultdict(list)
for elem in data:
d[feature(elem)].append(elem)

# Magic is no longer useful.
d = dict(d)

# Lookups are now safe.
print(d[some_feature])

2/
Converting back to a regular dictionary is cheap.

The hash values are reused rather than recomputed. The new dict is presized in a single step. The key and value pointers are copied rather than inserted. The only slow part is updating all the reference counts.

3/
Read 4 tweets
Feb 19, 2022
#Python structural pattern matching factlet:

Class patterns with positional arguments match by attribute lookup on the names in __match_args__.

Accordingly, they match normal attributes, descriptors, and virtual attributes implemented with __getattribute__ or __getattr__.

1/
class V:
'Virtual attribute'
__match_args__ = ('a',)
def __getattribute__(self, attr):
return 10 if attr == 'a' else 0

>>> match V():
... case V(10):
... print('hit')
...
hit

2/
class D:
'Descriptor'
__match_args__ = ('a',)
@property
def a(self):
return 20

>>> match D():
... case D(10):
... print('hit')
...
hit

3/
Read 5 tweets
Feb 8, 2022
#Python news: It was always awkward to write a type annotation for methods that returned self (an instance of the current class). As of yesterday, typing.Self was added to make this much easier and more readable.

It is a big win.

1/
This works great for methods that return self.

def set_scale(self, scale: float) -> Self:
self.scale = scale
return self

2/
The __enter__ method for context managers is sometimes a beneficiary:

def __enter__(self) -> Self:
return self

3/
Read 8 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(