Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use macros for float conversions #33

Closed
sidred opened this issue Feb 1, 2016 · 11 comments · Fixed by #35
Closed

use macros for float conversions #33

sidred opened this issue Feb 1, 2016 · 11 comments · Fixed by #35
Milestone

Comments

@sidred
Copy link
Contributor

sidred commented Feb 1, 2016

Define three macros


macro_rules! flt {
    ($x: expr) => {
        T::from($x).unwrap()
    };
}

macro_rules! f_0 {
    () => (T::zero());
}

macro_rules! f_1 {
    () => (T::one());
}

The the equation become easier to read

x: T::from(X_N).unwrap() * f_inv((T::one() / T::from(116.0).unwrap()) *
    (lab.l * T::from(100.0).unwrap() + T::from(16.0).unwrap()) +
    (T::one() / T::from(500.0).unwrap()) * lab.a * T::from(128.0).unwrap()),
y: T::from(Y_N).unwrap() * f_inv((T::one() / T::from(116.0).unwrap()) *
    (lab.l * T::from(100.0).unwrap() + T::from(16.0).unwrap())),
z: T::from(Z_N).unwrap() * f_inv((T::one() / T::from(116.0).unwrap()) *
    (lab.l * T::from(100.0).unwrap() + T::from(16.0).unwrap()) -
    (T::one() / T::from(200.0).unwrap()) * lab.b * T::from(128.0).unwrap()),

x: rgb.red * T::from(0.4124).unwrap() + rgb.green * T::from(0.3576).unwrap() + rgb.blue * T::from(0.1805).unwrap(),
y: rgb.red * T::from(0.2126).unwrap() + rgb.green * T::from(0.7152).unwrap() + rgb.blue * T::from(0.0722).unwrap(),
z: rgb.red * T::from(0.0193).unwrap() + rgb.green * T::from(0.1192).unwrap() + rgb.blue * T::from(0.9505).unwrap(),

becomes


x: flt!(X_N) * f_inv((f_1!() / flt!(116.0)) *
    (lab.l * flt!(100.0) + flt!(16.0)) +
    (f_1!() / flt!(500.0)) * lab.a * flt!(128.0)),
y: flt!(Y_N) * f_inv((f_1!() / flt!(116.0)) *
    (lab.l * flt!(100.0) + flt!(16.0))),
z: flt!(Z_N) * f_inv((f_1!() / flt!(116.0)) *
    (lab.l * flt!(100.0) + flt!(16.0)) -
    (f_1!() / flt!(200.0)) * lab.b * flt!(128.0)),

x: rgb.red * flt!(0.4124) + rgb.green * flt!(0.3576) + rgb.blue * flt!(0.1805),
y: rgb.red * flt!(0.2126) + rgb.green * flt!(0.7152) + rgb.blue * flt!(0.0722),
z: rgb.red * flt!(0.0193) + rgb.green * flt!(0.1192) + rgb.blue * flt!(0.9505),

@Ogeon
Copy link
Owner

Ogeon commented Feb 1, 2016

flt!(..) can be very useful. Have you tried implementing it as a function? Is the compiler smart enough to infer what T is in fn flt<T: NumCast, P: FromPrimitive>(prim: P) -> T?

The other two will just shave off a few characters, so I think we can live with T::one() and T::zero(). It's more self explanatory than f_1!() and f_0!().

@sidred
Copy link
Contributor Author

sidred commented Feb 1, 2016

What is the advantage of using a function instead of a macro?

@Ogeon
Copy link
Owner

Ogeon commented Feb 1, 2016

It's more generic than an assumed type T and they are often easier to reason about, since they can't just do anything. Tools like Racer doesn't work with macros for that reason, but they may do it in the future.

@sidred
Copy link
Contributor Author

sidred commented Feb 1, 2016

This works

pub fn flt<T: num::Float, P: num::ToPrimitive>(prim: P) -> T {
    num::NumCast::from(prim).unwrap()
}

// use as flt(2.90)
  • should we just use f instead of flt ?
  • should T::one() and T::zero() be changed to flt(0.0) and flt(1.0)? Since the compiler will optimize this away, performance will not be impacted.

@Ogeon
Copy link
Owner

Ogeon commented Feb 1, 2016

Cool! flt is a better clue to what it does. I would also prefer to keep T::one() and T::zero(), since they have an other level of guarantee and we don't have to make it more complicated for the compiler than necessary. 😄 They may also help type inference a bit more.

@sidred
Copy link
Contributor Author

sidred commented Feb 1, 2016

I'll make this change once we merge the yxy color. Otherwise there will be too many conflicts.

@Ogeon
Copy link
Owner

Ogeon commented Feb 1, 2016

Sounds good. One thing at the time 😄

@Ogeon Ogeon added this to the 0.2.1 milestone Feb 1, 2016
@sidred
Copy link
Contributor Author

sidred commented Feb 2, 2016

Using a function requires type annotations in some cases while the macro does not:

    pub fn new_u8(red: u8, green: u8, blue: u8) -> Rgb<T> {
        Rgb {
            red: flt<T,_>(red) / flt(255.0),
            green: flt<T,_>(green) / flt(255.0),
            blue: flt<T,_>(blue) / flt(255.0),
        }
    }

vs

    pub fn new_u8(red: u8, green: u8, blue: u8) -> Rgb<T> {
        Rgb {
            red: flt!(red) / flt!(255.0),
            green: flt!(green) / flt!(255.0),
            blue: flt!(blue) / flt!(255.0),
        }
    }

Do you have a preference for either?

@Ogeon
Copy link
Owner

Ogeon commented Feb 2, 2016

That's not too bad, IMO. It's significantly less noise, compared to the unwraps. I wonder if this would work:

let c255: T = flt(255.0);
Rgb {
    red: flt(red) / c255,
    green: flt(green) / c255,
    blue: flt(blue) / c255,
}

...or if it still needs to know the left hand side fist. Float implies Copy, so I have moved some repeated constants to variables like c255 above, to both clean things up a little and to lower the amount of repeated conversions of the same number.

In any case, I'm still biased towards a function, since it's possible in a reasonable way. I prefer to only use macros where nothing else helps, such as repetitive parts and/or in cases like try!.

@sidred
Copy link
Contributor Author

sidred commented Feb 2, 2016

No, the first parameter of a formula (or start of open brace) needs to be of known type T.

I have both methods working, if you want to check the difference
Function: https://github.com/sidred/palette/tree/use_float_fn
Macro: https://github.com/sidred/palette/tree/use_float_macro

@Ogeon
Copy link
Owner

Ogeon commented Feb 2, 2016

No, the first parameter of a formula (or start of open brace) needs to be of known type T.

I suspected as much, but I wanted to believe.

I have both methods working, if you want to check the difference

Nice! It looks like the function works fine in the critical parts, where the rest of the code is complicated enough as it is. My opinion is that it's completely acceptable. It may not be pretty, but it's at least isolated to the easy parts, as far as I can see, and the intent is clearer than with a macro.

@homu homu closed this as completed in #35 Feb 2, 2016
homu added a commit that referenced this issue Feb 2, 2016
use flt() function

Use function flt() to convert constants to trait Float type. Changed all T::from().unwrap() to use this flt() function defined in lib.rs. Makes the code easier to read, especially the formula's.

closes #33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants