printf and complex numbers #346

Closed
ViralBShah opened this Issue Jan 18, 2012 · 16 comments

Comments

Projects
None yet
3 participants
@ViralBShah
Member

ViralBShah commented Jan 18, 2012

How do we handle complex numbers in printf? It would be nice to handle them. Does C99 say anything on the topic?

julia> printf("%8.2f\n", randn()+im*randn())
type error: typeassert: expected Real, got Complex128
in anonymous, no file
in printf, /Users/viral/julia/j/printf.j:315
in run_repl, /Users/viral/julia/j/client.j:22
in _start, /Users/viral/julia/j/client.j:130

@ghost ghost assigned StefanKarpinski Jan 18, 2012

@JeffBezanson

This comment has been minimized.

Show comment
Hide comment
@JeffBezanson

JeffBezanson Jan 18, 2012

Member

Good example of why we could use standard formatting functions that can be overloaded for different types of numbers.

Member

JeffBezanson commented Jan 18, 2012

Good example of why we could use standard formatting functions that can be overloaded for different types of numbers.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 18, 2012

Member

I guess the expected behavior here would be to print each component with the given format specifier? That seems reasonable enough, at least from the user interface perspective. I'll have to think about how best to do this. It implies that you want not only your formatting function as a whole to be a generic function, which can be specialized on different argument combinations, but also that you want each individual formatter to be generic as well so that it can have overloaded definitions.

Member

StefanKarpinski commented Jan 18, 2012

I guess the expected behavior here would be to print each component with the given format specifier? That seems reasonable enough, at least from the user interface perspective. I'll have to think about how best to do this. It implies that you want not only your formatting function as a whole to be a generic function, which can be specialized on different argument combinations, but also that you want each individual formatter to be generic as well so that it can have overloaded definitions.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 18, 2012

Member

@JeffBezanson, is your idea that we just have a printf_f function that takes arguments maybe something like this:

printf_f(x::Float, width::Int, precision::Int, flags::ASCIIString) = ...
printf_f(x::Integer, width::Int, precision::Int, flags::ASCIIString) = ...
printf_f(x::Complex, width::Int, precision::Int, flags::ASCIIString) = ...

Two concerns about this:

  • compared to generating a custom function, it limits the amount of compile-time code specialization that can be done for each format specifier — you still need to handle width, precision and flags at print time.
  • it doesn't allow easy generalization of what to do with, e.g., complex numbers — there would have to be a complex method defined for every numeric format specifier, when what we really want to do is more something like a generic rule that any numeric format specifier be applied to the components of a complex number.

Another approach is to just say that the user has to deal with complex numbers. Writing this is not so bad:

z = randn()+im*randn()
printf("%8.2f+%8.2fim\n", real(z), imag(z))
Member

StefanKarpinski commented Jan 18, 2012

@JeffBezanson, is your idea that we just have a printf_f function that takes arguments maybe something like this:

printf_f(x::Float, width::Int, precision::Int, flags::ASCIIString) = ...
printf_f(x::Integer, width::Int, precision::Int, flags::ASCIIString) = ...
printf_f(x::Complex, width::Int, precision::Int, flags::ASCIIString) = ...

Two concerns about this:

  • compared to generating a custom function, it limits the amount of compile-time code specialization that can be done for each format specifier — you still need to handle width, precision and flags at print time.
  • it doesn't allow easy generalization of what to do with, e.g., complex numbers — there would have to be a complex method defined for every numeric format specifier, when what we really want to do is more something like a generic rule that any numeric format specifier be applied to the components of a complex number.

Another approach is to just say that the user has to deal with complex numbers. Writing this is not so bad:

z = randn()+im*randn()
printf("%8.2f+%8.2fim\n", real(z), imag(z))
@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 18, 2012

Member

An interesting generalization along similar lines would be doing things like this:

v = cumsum(randn(100))
printf("% 3d: %8.2f\n", 1:length(v), v)

which would automatically iterate over the given arguments in tandem. However, this could be done at the level of writing additional methods for printf itself rather than for the format function. It would allow the optimization of generating the format function only once for the entire vector.

Member

StefanKarpinski commented Jan 18, 2012

An interesting generalization along similar lines would be doing things like this:

v = cumsum(randn(100))
printf("% 3d: %8.2f\n", 1:length(v), v)

which would automatically iterate over the given arguments in tandem. However, this could be done at the level of writing additional methods for printf itself rather than for the format function. It would allow the optimization of generating the format function only once for the entire vector.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 18, 2012

Member

I think a slight level of indirection can accomplish what's requested here. The f"% 3d: %8.2f\n" part constructs an array of single-argument formatting functions that operate on the expected core type, in this case Real (all our numeric specifiers can handle integers or floating point numbers equally well directly). Let's call the individual formatting functions p1 and p2. Then the main function emitted for the format string looks like this:

function (x1,x2)
  printf1number(p1,x1)
  print(": ")
  printf1number(p2,x2)
  print('\n')
end

It is specialized for the number of arguments, the specific generated functions p1 and p2, and the stuff printed between that. The function printf1number has a core definition that is just this:

printf1number(p::Function, x::Real) = p(x)

That is, for real values, it just applies the formatting function p to x, which prints the value formatted. However, we can add a method for complex numbers too:

function printf1number(p::Function, x::Complex)
  p(real(x))
  print("+")
  p(imag(x))
  print("im")
end

This one method would enable all numeric format specifiers to print complex numbers.

Member

StefanKarpinski commented Jan 18, 2012

I think a slight level of indirection can accomplish what's requested here. The f"% 3d: %8.2f\n" part constructs an array of single-argument formatting functions that operate on the expected core type, in this case Real (all our numeric specifiers can handle integers or floating point numbers equally well directly). Let's call the individual formatting functions p1 and p2. Then the main function emitted for the format string looks like this:

function (x1,x2)
  printf1number(p1,x1)
  print(": ")
  printf1number(p2,x2)
  print('\n')
end

It is specialized for the number of arguments, the specific generated functions p1 and p2, and the stuff printed between that. The function printf1number has a core definition that is just this:

printf1number(p::Function, x::Real) = p(x)

That is, for real values, it just applies the formatting function p to x, which prints the value formatted. However, we can add a method for complex numbers too:

function printf1number(p::Function, x::Complex)
  p(real(x))
  print("+")
  p(imag(x))
  print("im")
end

This one method would enable all numeric format specifiers to print complex numbers.

@JeffBezanson

This comment has been minimized.

Show comment
Hide comment
@JeffBezanson

JeffBezanson Jan 18, 2012

Member

Unfortunately in that scenario I can no longer guarantee that the let optimization will occur. It seems manually writing printf("%8.2f+%8.2fim\n", real(z), imag(z)) or just using string(z) for the standard representation may be the best option. Let's go with that for now.

Member

JeffBezanson commented Jan 18, 2012

Unfortunately in that scenario I can no longer guarantee that the let optimization will occur. It seems manually writing printf("%8.2f+%8.2fim\n", real(z), imag(z)) or just using string(z) for the standard representation may be the best option. Let's go with that for now.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 18, 2012

Member

Yeah, I think that's fair. While very generic, this is an overly complicated approach for something that can be handled more flexibly on the user side.

Member

StefanKarpinski commented Jan 18, 2012

Yeah, I think that's fair. While very generic, this is an overly complicated approach for something that can be handled more flexibly on the user side.

@ViralBShah

This comment has been minimized.

Show comment
Hide comment
@ViralBShah

ViralBShah Jan 19, 2012

Member

It just seems inelegant for complex number printing to be handled in user code.

Member

ViralBShah commented Jan 19, 2012

It just seems inelegant for complex number printing to be handled in user code.

@JeffBezanson

This comment has been minimized.

Show comment
Hide comment
@JeffBezanson

JeffBezanson Jan 19, 2012

Member

Easiest would probably be to have a format character for it, but I'm not sure there are any letters left! :)

Member

JeffBezanson commented Jan 19, 2012

Easiest would probably be to have a format character for it, but I'm not sure there are any letters left! :)

@ViralBShah

This comment has been minimized.

Show comment
Hide comment
@ViralBShah

ViralBShah Jan 19, 2012

Member

What if we use the same one for reals, but print complex numbers when those are passed in - %f ?

-viral

On Jan 19, 2012, at 10:30 AM, JeffBezanson wrote:

Easiest would probably be to have a format character for it, but I'm not sure there are any letters left! :)


Reply to this email directly or view it on GitHub:
#346 (comment)

Member

ViralBShah commented Jan 19, 2012

What if we use the same one for reals, but print complex numbers when those are passed in - %f ?

-viral

On Jan 19, 2012, at 10:30 AM, JeffBezanson wrote:

Easiest would probably be to have a format character for it, but I'm not sure there are any letters left! :)


Reply to this email directly or view it on GitHub:
#346 (comment)

@JeffBezanson

This comment has been minimized.

Show comment
Hide comment
@JeffBezanson

JeffBezanson Jan 19, 2012

Member

Then what about Rational?

Member

JeffBezanson commented Jan 19, 2012

Then what about Rational?

@ViralBShah

This comment has been minimized.

Show comment
Hide comment
@ViralBShah

ViralBShah Jan 19, 2012

Member

Can't %f be a stand-in for all kinds of numbers passed in? It really just says that interpret the input as floating point with number of digits to print. Alternatively, for rational, we may just use %d.

-viral

On Jan 19, 2012, at 11:05 AM, JeffBezanson wrote:

Then what about Rational?


Reply to this email directly or view it on GitHub:
#346 (comment)

Member

ViralBShah commented Jan 19, 2012

Can't %f be a stand-in for all kinds of numbers passed in? It really just says that interpret the input as floating point with number of digits to print. Alternatively, for rational, we may just use %d.

-viral

On Jan 19, 2012, at 11:05 AM, JeffBezanson wrote:

Then what about Rational?


Reply to this email directly or view it on GitHub:
#346 (comment)

@JeffBezanson

This comment has been minimized.

Show comment
Hide comment
@JeffBezanson

JeffBezanson Jan 19, 2012

Member

Personally, I suspect printf just has too many features, making it harder than necessary to figure out how to factor the functionality.

Member

JeffBezanson commented Jan 19, 2012

Personally, I suspect printf just has too many features, making it harder than necessary to figure out how to factor the functionality.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 19, 2012

Member

Personally, I suspect printf just has too many features, making it harder than necessary to figure out how to factor the functionality.

Having struggled with implementing and then reimplementing it better for well over a week now, I can attest to this being true. I don't think we should add to the problem. I don't see where the benefit from being able to print complex values using %f is. There's all sorts of features like field width that stop making sense then. Does the width apply to each component of a complex value or to the whole thing? What if I don't like the way Julia chooses to format complex numbers? I don't want spaces around the + in the middle... I want to use i instead of im... I want to print complex numbers as a pair in parens. Once you head down this road, there's no end to feature requests. The printf spec is a standard, well-established, if overly complicated feature. We can implement it with some obvious improvements to usability and speed, but I think extending it in any substantial way is a bad idea.

Member

StefanKarpinski commented Jan 19, 2012

Personally, I suspect printf just has too many features, making it harder than necessary to figure out how to factor the functionality.

Having struggled with implementing and then reimplementing it better for well over a week now, I can attest to this being true. I don't think we should add to the problem. I don't see where the benefit from being able to print complex values using %f is. There's all sorts of features like field width that stop making sense then. Does the width apply to each component of a complex value or to the whole thing? What if I don't like the way Julia chooses to format complex numbers? I don't want spaces around the + in the middle... I want to use i instead of im... I want to print complex numbers as a pair in parens. Once you head down this road, there's no end to feature requests. The printf spec is a standard, well-established, if overly complicated feature. We can implement it with some obvious improvements to usability and speed, but I think extending it in any substantial way is a bad idea.

@StefanKarpinski

This comment has been minimized.

Show comment
Hide comment
@StefanKarpinski

StefanKarpinski Jan 19, 2012

Member

For example, our printf can do these tricks:

julia> sprintf("%d",realmax())
"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368"

julia> sprintf("%f",typemax(Int64))
"9223372036854775807.000000"

julia> sprintf("%f",typemax(Uint64))
"18446744073709551615.000000"
Member

StefanKarpinski commented Jan 19, 2012

For example, our printf can do these tricks:

julia> sprintf("%d",realmax())
"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368"

julia> sprintf("%f",typemax(Int64))
"9223372036854775807.000000"

julia> sprintf("%f",typemax(Uint64))
"18446744073709551615.000000"
@ViralBShah

This comment has been minimized.

Show comment
Hide comment
@ViralBShah

ViralBShah Jan 30, 2012

Member

Closing this since the discussion clearly shows that we'd rather not add more functionality to printf.

Member

ViralBShah commented Jan 30, 2012

Closing this since the discussion clearly shows that we'd rather not add more functionality to printf.

@ViralBShah ViralBShah closed this Jan 30, 2012

StefanKarpinski pushed a commit that referenced this issue Feb 8, 2018

Merge pull request #346 from traktofon/complex-bswap
Backport bswap method for complex numbers.

KristofferC added a commit that referenced this issue Jun 6, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment