Add experimental f128 and f256 modes for /calc
Run cargo test / Run tests (push) Successful in 4m1s
Build Docker Package / build (push) Successful in 6m7s
Build Docker Package / build (release) Successful in 7m20s

This commit is contained in:
Elias Wendland
2026-07-05 01:06:49 +02:00
parent b3ada54689
commit 24205326bf
3 changed files with 130 additions and 3 deletions
+4 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tg-dev-srv-bot"
version = "0.1.1"
version = "0.1.2"
edition = "2024"
license = "GPL-3.0-or-later"
description = "Bot for the TeenGovernment Development Server"
@@ -12,6 +12,9 @@ homepage = "https://git.ewenlau.net/ewenlau/tg-dev-srv-bot"
[dependencies]
dotenv = "0.15.0"
exmex = "0.21.0"
f128 = "0.2.9"
f256 = "0.11.2"
num-traits = "0.2.19"
poise = "0.6.2"
regex = "1.12.4"
rug = "1.30.0"
Executable
BIN
View File
Binary file not shown.
+126 -2
View File
@@ -3,8 +3,110 @@ use tracing::{debug, trace, info};
use crate::shared_functions::trace_message;
use regex::Regex;
use std::sync::LazyLock;
use std::str::FromStr;
use std::fmt::{Display, Formatter};
use f128::f128;
use exmex::{Express, FlatEx, MakeOperators, Operator, BinOp};
use num_traits::float::Float;
static find_sci_notation_re: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"([0-9]+(?:\.[0-9]+)?)e([+-]?[0-9]+)").unwrap());
#[derive(Clone, Copy, Debug)]
pub struct F128(pub f128);
impl Default for F128 {
fn default() -> Self {
F128(f128::new(0.0))
}
}
impl FromStr for F128 {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match f128::parse(s) {
Ok(v) => Ok(F128(v)),
Err(e) => Err(format!("{:?}", e))
}
}
}
impl Display for F128 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct F128OpsFactory;
impl MakeOperators<F128> for F128OpsFactory {
fn make<'a>() -> Vec<Operator<'a, F128>> {
vec![
Operator::make_bin("^", BinOp { apply: |a, b| F128(a.0.powf(b.0)), prio: 4, is_commutative: false }),
Operator::make_bin("*", BinOp { apply: |a, b| F128(a.0 * b.0), prio: 2, is_commutative: true }),
Operator::make_bin("/", BinOp { apply: |a, b| F128(a.0 / b.0), prio: 3, is_commutative: false }),
Operator::make_bin_unary("+", BinOp { apply: |a, b| F128(a.0 + b.0), prio: 0, is_commutative: true }, |a| a),
Operator::make_bin_unary("-", BinOp { apply: |a, b| F128(a.0 - b.0), prio: 1, is_commutative: false }, |a| F128(-a.0)),
Operator::make_unary("sin", |a| F128(a.0.sin())),
Operator::make_unary("cos", |a| F128(a.0.cos())),
Operator::make_unary("tan", |a| F128(a.0.tan())),
Operator::make_unary("floor", |a| F128(a.0.floor())),
Operator::make_unary("round", |a| F128(a.0.round())),
Operator::make_unary("ceil", |a| F128(a.0.ceil())),
Operator::make_unary("trunc", |a| F128(a.0.trunc())),
Operator::make_unary("fract", |a| F128(a.0.fract())),
Operator::make_unary("exp", |a| F128(a.0.exp())),
Operator::make_unary("sqrt", |a| F128(a.0.sqrt())),
Operator::make_unary("cbrt", |a| F128(a.0.cbrt())),
Operator::make_unary("ln", |a| F128(a.0.ln())),
Operator::make_unary("log2", |a| F128(a.0.log2())),
Operator::make_unary("log10", |a| F128(a.0.log10())),
Operator::make_unary("abs", |a| F128(a.0.abs())),
Operator::make_unary("signum", |a| F128(a.0.signum())),
Operator::make_constant("PI", F128(f128::new(std::f64::consts::PI))),
Operator::make_constant("π", F128(f128::new(std::f64::consts::PI))),
Operator::make_constant("E", F128(f128::new(std::f64::consts::E))),
Operator::make_constant("e", F128(f128::new(std::f64::consts::E))),
Operator::make_constant("TAU", F128(f128::new(std::f64::consts::TAU))),
Operator::make_constant("τ", F128(f128::new(std::f64::consts::TAU))),
]
}
}
#[derive(Clone, Debug)]
pub struct F256OpsFactory;
impl MakeOperators<f256::f256> for F256OpsFactory {
fn make<'a>() -> Vec<Operator<'a, f256::f256>> {
vec![
Operator::make_bin("^", BinOp { apply: |a, b| a.powf(&b), prio: 4, is_commutative: false }),
Operator::make_bin("*", BinOp { apply: |a, b| a * b, prio: 2, is_commutative: true }),
Operator::make_bin("/", BinOp { apply: |a, b| a / b, prio: 3, is_commutative: false }),
Operator::make_bin_unary("+", BinOp { apply: |a, b| a + b, prio: 0, is_commutative: true }, |a| a),
Operator::make_bin_unary("-", BinOp { apply: |a, b| a - b, prio: 1, is_commutative: false }, |a| -a),
Operator::make_unary("sin", |a| a.sin()),
Operator::make_unary("cos", |a| a.cos()),
Operator::make_unary("tan", |a| a.tan()),
Operator::make_unary("floor", |a| a.floor()),
Operator::make_unary("round", |a| a.round()),
Operator::make_unary("ceil", |a| a.ceil()),
Operator::make_unary("trunc", |a| a.trunc()),
Operator::make_unary("fract", |a| a.fract()),
Operator::make_unary("exp", |a| a.exp()),
Operator::make_unary("sqrt", |a| a.sqrt()),
Operator::make_unary("cbrt", |a| a.cbrt()),
Operator::make_unary("ln", |a| a.ln()),
Operator::make_unary("log2", |a| a.log2()),
Operator::make_unary("log10", |a| a.log10()),
Operator::make_unary("abs", |a| a.abs()),
Operator::make_unary("signum", |a| a.signum()),
Operator::make_constant("PI", f256::f256::from(std::f64::consts::PI)),
Operator::make_constant("π", f256::f256::from(std::f64::consts::PI)),
Operator::make_constant("E", f256::f256::from(std::f64::consts::E)),
Operator::make_constant("e", f256::f256::from(std::f64::consts::E)),
Operator::make_constant("TAU", f256::f256::from(std::f64::consts::TAU)),
Operator::make_constant("τ", f256::f256::from(std::f64::consts::TAU)),
]
}
}
static FIND_SCI_NOTATION_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"([0-9]+(?:\.[0-9]+)?)e([+-]?[0-9]+)").unwrap());
#[derive(Debug, poise::ChoiceParameter)]
pub enum Precision {
@@ -12,6 +114,18 @@ pub enum Precision {
Bits32,
#[name = "64-bit"]
Bits64,
#[name = "128-bit"]
Bits128,
#[name = "256-bit"]
Bits256,
}
#[derive(Debug, poise::ChoiceParameter)]
pub enum Mode {
#[name = "Floating Point"]
Float,
#[name = "Integer"]
Int,
}
/// Calculator
@@ -28,12 +142,20 @@ pub async fn calc(ctx: Context<'_>,
let response_message = ctx.say(msg).await?;
let start_time = std::time::Instant::now();
trace!("Saving start time: {:?}", start_time);
let processed_expr = find_sci_notation_re.replace_all(&expression, "($1 * 10^($2))");
let processed_expr = FIND_SCI_NOTATION_RE.replace_all(&expression, "($1 * 10^($2))");
trace!("Processed expression: {}", processed_expr);
let value = match precision {
Some(Precision::Bits32) => exmex::eval_str::<f32>(&processed_expr).map_err(|err| err.to_string())?.to_string(),
Some(Precision::Bits64) => exmex::eval_str::<f64>(&processed_expr).map_err(|err| err.to_string())?.to_string(),
Some(Precision::Bits128) => {
let expr = exmex::FlatEx::<F128, F128OpsFactory>::parse(&processed_expr).map_err(|err| err.to_string())?;
expr.eval(&[]).map_err(|err| err.to_string())?.to_string()
},
Some(Precision::Bits256) => {
let expr = exmex::FlatEx::<f256::f256, F256OpsFactory>::parse(&processed_expr).map_err(|err| err.to_string())?;
expr.eval(&[]).map_err(|err| err.to_string())?.to_string()
},
None => exmex::eval_str::<f64>(&processed_expr).map_err(|err| err.to_string())?.to_string(),
};
let end_time = std::time::Instant::now();
@@ -43,6 +165,8 @@ pub async fn calc(ctx: Context<'_>,
let precision_str = match precision {
Some(Precision::Bits32) => "32-bit".to_string(),
Some(Precision::Bits64) => "64-bit".to_string(),
Some(Precision::Bits128) => "128-bit".to_string(),
Some(Precision::Bits256) => "256-bit".to_string(),
None => "64-bit".to_string(),
};
let msg = format!("{} = {} \n-# Precision: {} Compute time : {:?}", expression, value, precision_str, duration);