Coder Social home page Coder Social logo

astro-float's People

Contributors

benjamin-cates avatar oovm avatar stencillogic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

astro-float's Issues

Add a `BigFloat` with const generic precision

A lot of the time, needed precision might be known ahead of runtime. Const generics might be able to make use of something like this:

struct KnownBigFloat<const WORDS: usize = 2> {
    inner: KnownFlavor<WORDS>,
}

#[derive(Debug)]
enum KnownFlavor<const WORDS: usize> {
    Value(KnownBigFloatNumber<WORDS>),
    NaN(Option<Error>),
    Inf(Sign), // signed Inf
}

pub(crate) struct KnownBigFloatNumber<const WORDS: usize> {
    e: Exponent,
    s: Sign,
    m: KnownMantissa<WORDS>,
    inexact: bool,
}

pub(crate) struct KnownMantissa<const WORDS: usize> {
    m: [Word; WORDS],
}

I expect this would have a noticeable performance increase as well - mantissa is smaller for <= 128 bits of precision, allocation-free, no indirection, and probably some nicer optimizations.

A downside is this isn't as elegant as it could be with the unstbale generic_const_exprs - ideally you could specify BITS in the const generic and store m: [Word; BITS / WORD_BIT_SIZE], but that won't be stable for a while.

Convert BigFloat to BigUint (from num_bigint)

Thanks for the lib! It has been immensely helpful.

I am running into issues converting a BigFloat instance to BigUint (from num_bigint). My naive try is

let mut consts = Consts::new().unwrap();

let mut value = BigFloat::from(1.2);

let (sign, radix_repr, ex) = value
  .round(0, ROUNDING_MODE)
  .convert_to_radix(astro_float::Radix::Dec, ROUNDING_MODE, &mut consts)
  .unwrap();
  
// Assume sign is always positive
let value_biguint = BigUint::from_radix_be(&radix_repr, 10).unwrap();

I though that setting to n to 0 (i.e. fractional digits are decimal) will round the value to 1, hence mantissa will 1 and exp will be 0. But this isn't the case. Probably I am misreding the docs. Can you point me to a function using which I can round the value towards 0 and mantissa will drop rounded-off digits?

Replace SmallVec with Vec

SmallVec does not show performance benefits compared to Vec, so, the dependency on SmallVec should be excluded.

Implement ^ operator in expr macro

Implement binary operator ^ which given n^m computes n to the power of m. Consider using BigFloatNumber::powi for unsigned integer m, BigFloatNumber::powsi for signed integer m, and BigFloatNumber::pow in other cases.

Review Unsafe code sections.

Review unsafe code sections and try replacing unsafe with safe analogs having the same or better performance. If the performance drops unsafe should stay.

`sinh` of a large negative number returns positive infinity

Hello! I really like this package and I've been working with it a lot, however I have found a bug :(

Reproducible example in astro-float 0.9.1

use astro_float
#[test]
fn test_sinh() {
    let prec = 256;
    let mut cc = astro_float::Consts::new().unwrap();
    let sinh_neg = astro_float::BigFloat::from_f64(-1e50, prec).sinh(
        prec,
        astro_float::RoundingMode::None,
        &mut cc,
    );
    // Passes :(
    assert!(sinh_neg.is_inf_pos());
    // Fails :(
    assert!(sinh_neg.is_inf_neg());
}

Basically the sinh of any really negative number will return positive infinity when it should return negative infinity. However the sinh of negative infinity is correct. I believe this is because the exponent overflow error from 1/(e^x) does not swap signs. Basically if x is really negative, the reciprocal operation on positive zero will overflow to positive infinity and the offending line of code (below) will pass on the positive overflow onto the return value even though it is being subtracted from the final result later.

let xe = ex.reciprocal(p_x, RoundingMode::None)?;

Link to line of code that's broken

It was likely intended that the question mark operator would pass the error into the map_err function but it actually just returns it.

I think the best solution would be to replace the question mark operator with a match statement that just sets the overflow sign to negative (this is probably the fastest because it is doing the match statement anyway).

I am willing to submit a pull request to fix this or if you want to fix it that would be cool too.

Add convenience functions for conversion to primitive integer types.

Consider adding conversion functions to primitive integer types.
Possible signatures (<int_type> is i8, i16, i32, i64, i128, u8, u16,u32, u64, u128):

  • BigFloat::try_to_<int_type>(&self, RoundingMode) -> Result<<int_type>, Error>: returns Error if BigFloat's absolute values is larger than the the value <int_type> can hold.
  • BigFloat::to_<int_type>_saturating(&self, RoundingMode) -> <int_type>: if the value of BigFloat is larger than the value that <int_type> can hold, clamp the value to the maximum value of <int_type>.

Use target pointer width rather than x86

The code

/// A word.
#[cfg(not(target_arch = "x86"))]
pub type Word = u64;
/// Doubled word.
#[cfg(not(target_arch = "x86"))]
pub type DoubleWord = u128;
/// Word with sign.
#[cfg(not(target_arch = "x86"))]
pub type SignedWord = i128;
/// A word.
#[cfg(target_arch = "x86")]
pub type Word = u32;
/// Doubled word.
#[cfg(target_arch = "x86")]
pub type DoubleWord = u64;
/// Word with sign.
#[cfg(target_arch = "x86")]
pub type SignedWord = i64;
has a lot of code that is configured based on x86 or not(x86). Is there a reason that x86 is special, or could this instead use #[cfg(target_pointer_width = 64)]/#[cfg(target_pointer_width = 32)]?

Addition of specific values in `expr!` hangs astro_float

While happily using this library, we noticed a program would hang in certain conditions and narrowed it down to this snippet:

use astro_float::{ctx::Context, expr, BigFloat, Consts, RoundingMode, Sign};

fn main() {
    let mut precision_context = Context::new(
        128,
        RoundingMode::ToEven,
        Consts::new().expect("Constants cache initialized"),
    );

    let x = BigFloat::from_raw_parts(
        &[9441246966859219331u64, 13943995520284385473u64],
        128,
        Sign::Pos,
        -5,
        true,
    );
    let y = BigFloat::from_raw_parts(
        &[145249953336295741u64, 9295997013522923649u64],
        128,
        Sign::Neg,
        -3,
        true,
    );

    println!("{}", expr!(y + x, &mut precision_context));
}

Version of astro-float:

astro-float = "0.7.1"

Platform is windows. Here are the compiler versions I can reproduce it with:

rustc 1.75.0-nightly (a2f5f9691 2023-11-02)
rustc 1.73.0 (cc66ad468 2023-10-03)

Proposal for a public `BigFloat::to_f64`

I have a use case where I need to go back and forth between BigFloat and f64. fn to_f64(&self) -> f64 would be perfect.

For context there is an existing pub(crate) implementation.

pub(crate) fn to_f64(&self) -> f64 {
.

I would like to propose cleaning up this method and tweak it to follow Rust's convention with casting between f32 and f64. According to the nomicon

  • Casting from an f32 to an f64 is perfect and lossless
  • Casting from an f64 to an f32 will produce the closest possible f32
    • if necessary, rounding is according to roundTiesToEven mode ***
    • on overflow, infinity (of the same sign as the input) is produced

*** as defined in IEEE 754-2008 §4.3.1: pick the nearest floating point number, preferring the one with an even least significant digit if exactly halfway between two floating point numbers.

Here the same rules can be followed.

  • If precision is <= 52, the conversion is lossless.
  • Otherwise produce the closest possible f64 (includes subnormals)
    • if necessary, rounding is according to roundTiesToEven mode
    • on overflow, infinity (of the same sign as the input) is produced

I am open to working on this contribution.

Typedefs Don't Support non-x86

I built on/for an M1 Mac system:

rustup target add aarch64-apple-darwin
cargo check --target=aarch64-apple-darwin

(check won't require a local toolchain, linker, SDKs, etc)

I see errors like this, followed by cascading issues which I think are related:

error[E0432]: unresolved import `crate::defs::Word`
 --> astro-float-num/src/common/buf.rs:4:5
  |
4 | use crate::defs::Word;
  |     ^^^^^^^^^^^^^^^^^ no `Word` in `defs`

I think the crux is snippets like this:

/// A word.
#[cfg(target_arch = "x86_64")]
pub type Word = u64;

// ...

/// A word.
#[cfg(target_arch = "x86")]
pub type Word = u32;

There's no base case, so as soon as you compile for not-x86 everything falls apart. You could add more targets to the list, but you could also use target_pointer_width instead of target_arch to make these decisions.
See: https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width

Improve performance of Mantissa::pow_mod

astro_float_num::mantissa::mantissa::Mantissa::pow_mod calls mul_mod function in a cycle with the same argument n. Each mul_mod performs division by n. It may be more performant to replace the division with the multiplication by 1/n. I.e. consider implementing Barret reduction.

Unexpected rounding behaviour.

I'm surprised by the rounding behaviour used in BigFloat.round(...). rounding 0.99 -> 0 seems very unnatural to me.

use astro_float::{BigFloat, RoundingMode};

fn main() {
    let result = BigFloat::from_f64(0.99, 64).round(0, RoundingMode::ToEven);
    println!("{result}");
}

Output:

0.0

Values 10x smaller with BigFloat::from_f64();

let day2 = BigFloat::from_f64(symbol.data[len - 2].close, 256);
println!("{} {}", symbol.data[len - 2].close, day2);

here i print 2 values, i expected they will be same but,

image

one is 10x smaller
why?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.