Skip to content

Releases: tommyettinger/digital

0.4.6 "Happy Holi-Base and Merry Trig-Mas"

25 Dec 09:50
Compare
Choose a tag to compare

A little present under your tree: a new release of digital! Just what you always wanted, right? This release speeds up lots of trig functions (especially sin(), cos(), and the degree/angle variations), and shouldn't be significantly slower on any other trig methods. Accuracy for these is nearly identical, and still good. What's this? In your stocking, another present? There's also many improvements to Base, adding methods intended for code generation! These write numbers in the int, long, double, float, or char literal format appropriate to their parameter or return type, in a way that can be read back into Java source code. These methods are static, and make use of various existing static Base values. There are also some simple new convenience methods in TextTools to repeat a String a set number of times.

It's been quite a year for digital, with lots of optimizations and corrections applied to the trigonometry code in particular, but hopefully next year will be mostly stable. Or it could wind up expanding scope somehow, who knows?

0.4.5 "Ride The Wave"

02 Dec 06:02
Compare
Choose a tag to compare

This release consists mainly of fixes for the wave functions added in 0.4.4, and a small speed + accuracy boost to some sin() methods. The triangleWave(), squareWave(), and sawtoothWave() methods were missing some important parentheses, and behaved very badly before (they are fine now). Methods like sin(), sinDeg(), sinTurns(), for float and for double types, should be a little faster and more precise by using the sine table (instead of shifting and using the cosine table). There aren't changes to any sinSmooth() or sinSmoother() variants, nor any cosine methods. There are also some small optimizations in Interpolations, affecting (fittingly for the wave theme) the sine, sineIn, and sineOut Interpolator instances; the visible difference for the new interpolations should be miniscule, if noticeable at all, but zoomed-in usage might see that these have a tiny staircase effect, with one step for every 1/4096.0 change to the input. This also updates to Gradle 8.5, so it could be built with Java 21, if anyone wants to do that.

0.4.2 "Trig Or Treat"

02 Nov 02:56
Compare
Choose a tag to compare

A day after Halloween, here's a little treat! Because I didn't bother to post a GitHub release for versions between 0.3.0 and this one (0.4.2), the changelog for all of this would be... quite long. As a quick summary for what came before this...

  • There's better joining code in Base.
  • TextTools is new with code used by Base and useful in toString().
  • Hasher uses a similar but not-identical algorithm now.
  • You can specify a limit on how many chars to use in Base.decimal().

What's new in this release? Mostly improvements to the speed and accuracy of TrigTools! Now we store both a sine table (mostly for backwards-compatibility, but it is used internally as well) and a cosine table (useful because cosine is symmetric across 0.0, so negative inputs are one quick Math.abs() call away from using the same code as positive ones). Calling TrigTools.cos() is now slightly faster than TrigTools.sin(), which was different before; cos() used to take almost twice as long as sin(), but that is only a roughly-3-ns difference. The sinSmoother() and cosSmoother() methods are also faster. I haven't actually benchmarked tanSmoother(), but it's nearly the same.

The other new change in this release is in BitConversion. I've been adding a few new methods recently that benefit from what JS can do now. JS provides Math.clz32(), but GWT doesn't use it, so we provide BitConversion.countLeadingZeros(n) (where n can be int or long), and that uses clz32() on JS and Integer.numberOfLeadingZeros() (or Long's version) on other platforms. Similarly, there's BitConversion.countTrailingZeros(n). These two methods count how many contiguous 0 bits are present in a number starting from either the leading side (the sign bit) or the trailing side (the 1's place). They're surprisingly useful for such a seemingly-narrow scope; some usage simply needs to measure how many bits there are before a 1 bit, and other usage... is more arcane.

I hope this release is useful! Happy Halloween!

Oh, that was yesterday. Uh, Feliz Dia de los Muertos, for another few hours!

0.3.0 "Triple Threat"

15 Apr 05:21
Compare
Choose a tag to compare

This release mainly does three things: It fixes int printing on GWT for some inputs to Base, it improves handling of MathTools.lerpAngle() for large and/or negative angles, and it has a major addition to Hasher. The first two are self-explanatory, but Hasher needs to be described a bit. Before, a Hasher was almost always limited to hashing a complete array, with empty space at either end unable to be treated specially or ignored. Now, the hash() and hash64() methods can hash a subrange of data, starting at an index in the array and hashing a given count of items. This was a pretty big change, since Hasher is about 2700 lines of Java, and many needed to be updated. It is also a breaking change if you used any of a few methods: hash(char[], int, int) and hash(CharSequence, int, int) used to take a start index and end index, but now they take a start index and the length of the span to hash. This is also true for the hash64() versions of those, and the variants on both of those that are static and take a long seed. Other than updating Gradle, that's all this does. Enjoy!

0.2.0 "Ten Finger Discount"

05 Mar 08:21
Compare
Choose a tag to compare

This release adds several new methods to Base, allowing it to human-readably print float and double numbers as base-10 (regardless of which Base you use) with general(), decimal(), scientific(), and friendly(), as well as keeping the earlier machine-readable signed() and unsigned() methods, which change depending on the individual Base. The base-10 printing methods differ in when they transition from decimal (12345.0) to scientific notation (1.2345E+4); decimal() always uses decimal, even for extremely large or small numbers, scientific() always uses scientific notation, general() changes to scientific for exponents lower than -3 or higher than +7, and friendly() changes to scientific for exponents lower than -10 or higher than +10 (it is friendlier to human readers for values in ranges they could typically encounter).

There is a breaking change here; readFloat() and readDouble() now handle the four human-readable values only (the same is true for splitting and joining methods that work with float[] double[], and their 2D versions), while reading/splitting/joining methods have variants with Exact in the name now that read in or produce the signed() or unsigned() output. This caused a minor break in the related library juniper, which is fixed in juniper 0.2.0, and also affected a few other places, but the fix is at least easy. Either a) change readFloat() to readFloatExact() and readDouble() to readDoubleExact(), or b) if you want readable floats and doubles, change signed() and/or unsigned() given floats or doubles to one of the four methods above. However, signed() and unsigned() are more efficient, and reading their output doesn't require calling Double.parseDouble(String) with a substring of the input (which allocates a String, unfortunately).

I learned a lot here about printing and reading floating-point values; namely, that it is extremely hard and should be left to dedicated libraries. I found out that the Apache Commons Lang code to validate whether a String contains a number or not has at least one bug in it. If Apache Commons can't get it right, there is no way I am going to get it right on my own. So, this relies on Ryu to handle printing floats and doubles (with small modifications to support friendly() and to avoid some allocations). Ryu is fairly lightweight, and doesn't have a Maven dependency that I could find, so I just copied in the few files that it needs. We also rely on a modified version of the Apache Commons code to check if numbers can be parsed, and use it to determine the bounds of a number in a String before parsing that with the JDK.

0.1.5 "Filler Up"

30 Nov 23:58
Compare
Choose a tag to compare

This release is small, but adds one tiny method to BitConversion so it can be emulated on GWT, adds constants for the vertices and faces of the five Platonic solids in 3D, and adds some more "filling methods" to ArrayTools. The new BitConversion method is int imul(int, int), which acts exactly like int multiplication on non-GWT platforms. But, GWT defaults to behaving strangely when both ints are fairly large -- where on desktop, 0x7fffffff * 0x7fffffff overflows and returns 1, on GWT it vastly exceeds the normal limits for an int and returns 4611686014132420600. The new BitConversion.imul() method calls the JS method Math.imul(), and so it acts like that does and like desktop Java does, overflowing and returning 1 in the aforementioned case. The constants for the Platonic solids, like the icosahedron and cube, are in ShapeTools; there's double and float variants, and versions with an edge length of 1 as well as versions that are inscribed in a unit sphere. The filling methods in ArrayTools allow you to get various placeholder Strings (useful for testing), in arrays or random-accessed. You can access Greek letter names, names of demons from the Ars Goetia, and the names of 118 chemical elements, either all at once or in individual sets. Many of these Strings were used already to seed the 428 Hasher instances in Hasher.predefined, and there are more than twice as many Hashers as there were.

0.1.4 "Breakcore Fixwave"

05 Oct 23:25
Compare
Choose a tag to compare

This release breaks some backwards compatibility in order to fix compatibility (and avoid parser bugs) in related libraries. Changes are mostly limited to Base:

  • Base86 had its digits change because some were invalid in the String serialization juniper uses.
  • Scrambled bases also can use different digits, because some were invalid sometimes.
  • Writing an unsigned float or double prefixes with . now (they are still read by readDouble()).
  • Writing a signed float or double is often much shorter, doesn't have a prefix, and uses a reversed byte order (it is read the same way).

There are some added APIs:

  • BitConversion.doubleToReversedLongBits() and BitConversion.reversedLongBitsToDouble() are just like their float-int counterparts, but for double-long conversions.
  • AlternateRandom is here for code that doesn't use juniper but does use ArrayTools.shuffle() a lot. It uses the same algorithm as PasarRandom, which should be in juniper 0.1.5 soon, but doesn't have as many added features.
  • MathTools.boundedInt() and MathTools.boundedLong() allow taking a typically-random number and getting a typically-smaller, bounded random number from it with some guarantee of somewhat random results for random inputs.

Even though this is a point release, it should probably be 0.2.0 because it changes some behavior. Whoops. The behavior was broken, so a fix was required.

0.1.3 "Wisdom Of The Ancients"

26 Sep 03:50
Compare
Choose a tag to compare

This release almost entirely affects TrigTools. The lookup table it uses for sin() and cos() is now more precise, and for the times when a LUT isn't appropriate (such as when cache isn't readily available, or the limited possible return values are a noticeable issue), there are now sinSmooth() and cosSmooth() versions of sin() and cos() that don't use a table. These also have float and double versions, as well as versions that use radians, degrees, and turns. Big thanks to 7th-century astronomer Bhaskara I for the basis for the smooth function, and WimC from Stack Exchange for improving on it.

0.1.2 "Spray Tan"

08 Sep 22:37
Compare
Choose a tag to compare

This release only changes the TrigTools.tan() methods and related versions in turns and degrees. The earlier version used an optimization on sin()/cos(), but even small errors in the sine table became grossly magnified in the tan() results. This was starkly noticeable in a demo for juniper's random number distributions, where the Cauchy distribution (which uses tan() in its calculation) went from looking like this with the old tan(), to looking like this with the new tan(). The measured absolute, relative, and maximum error values are all better with the current tan(), though it isn't as fast on Java 8 (still an improvement over Math.tan() in speed). Like the old tan(), the error gets significantly worse as the correct result gets larger, but not as badly as before.

0.1.1 "Chart A Course"

29 Aug 01:52
Compare
Choose a tag to compare

This release is very small; it adds a quick way to calculate the nth Fibonacci number and a more-precise way to calculate the nth root of a float. The MathTools.fibonacci() method takes an int or long and returns that type; it uses something close to Binet's algorithm. However, it has been subtly adjusted to counteract floating-point error, so it can return correct results for inputs up to and including 77 instead of 71 (which is what it would manage without adjustment). The MathTools.nthrt() method gets raises the first input to the reciprocal of the second input, just as you would normally do for something that gets the nth root. It then goes beyond that to correct results that are within a tolerance of an integer, returning the close-by int value rather than a potentially-slightly-off float value.

That's all there is here! Mostly I wanted to keep digital up-to-date as I update related libraries.