format and printf can't be used with BigInt

676 views
Skip to first unread message

Andrea Tortorella

unread,
Jul 27, 2011, 2:45:41 PM7/27/11
to Clojure
Hi everyone,
I don't know where to post about bugs (if this is a bug).
Anyway in clojure 1.3 with the new numerics:

(format "%d" 2N)

throws IllegalFormatConversionException, is it a bug? are there any
workarounds?

Alan Malloy

unread,
Jul 27, 2011, 4:33:45 PM7/27/11
to Clojure
Unlikely to change anytime soon: format just calls
java.util.Formatter.format; if you look at the source of that, it
tests for each built-in numeric type specially, instead of using
Number. But you can probably use the %s specifier, since converting it
to a string is unlikely to do much harm.

Ken Wesson

unread,
Jul 27, 2011, 7:32:03 PM7/27/11
to clo...@googlegroups.com

Of course none of the nice number-specific formatting options are
available then. If you don't mind loss of precision in
less-significant digits, you might use %f and (double the-number) to
format its coercion to double.

But according to
http://download.oracle.com/javase/6/docs/api/java/util/Formatter.html
Formatter *is* supposed to work with BigInteger with %d (and other
integer-formatting options, and with BigDecimal with %f and other
float-formatting options). So I don't know what's going wrong here,
other than that it also says IllegalFormatConversionException gets
thrown if the format symbol and the parameter are mismatched.

The JavaSE 6 Formatter works properly when invoked explicitly via interop:

=> (.format (java.util.Formatter.) "%d" (into-array Object [(bigint 2)]))
#<Formatter 2>

(2N isn't recognized as a BigInteger literal by Clojure 1.2, it seems.)

In my copy of Clojure 1.2, format also seems to work:

=> (format "%d" (bigint 2))
"2"

So this could be a bug in 1.3 the OP is running into, or else 2N is
turning into something other than what (bigint 2) evaluates to.

=> (format "%d" #=(bigint 2))
"2"

also works for me, and is more closely equivalent to what the OP is
trying to do (as the bigint is embedded directly in the code via the
reader rather than constructed on the fly at eval time).

It looks like either 2N is doing something unexpected or the format
function somehow broke in 1.3.

--
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

Sean Corfield

unread,
Jul 27, 2011, 8:25:17 PM7/27/11
to clo...@googlegroups.com
On Wed, Jul 27, 2011 at 4:32 PM, Ken Wesson <kwes...@gmail.com> wrote:
> => (.format (java.util.Formatter.) "%d" (into-array Object [(bigint 2)]))
> #<Formatter 2>
>
> (2N isn't recognized as a BigInteger literal by Clojure 1.2, it seems.)
>
> In my copy of Clojure 1.2, format also seems to work:
>
> => (format "%d" (bigint 2))
> "2"

In Clojure 1.2:

(type (bigint 2)) => java.math.BigInteger

In Clojure 1.3:

(type (bigint 2)) => clojure.lang.BigInt
(type 2N) => clojure.lang.BigInt

clojure.lang.BigInt != java.math.BigInteger which is why format no longer works.

You can do this:

(format "%d" (.toBigInteger 2N))
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Ken Wesson

unread,
Jul 27, 2011, 8:56:53 PM7/27/11
to clo...@googlegroups.com
On Wed, Jul 27, 2011 at 8:25 PM, Sean Corfield <seanco...@gmail.com> wrote:
> On Wed, Jul 27, 2011 at 4:32 PM, Ken Wesson <kwes...@gmail.com> wrote:
>> => (.format (java.util.Formatter.) "%d" (into-array Object [(bigint 2)]))
>> #<Formatter 2>
>>
>> (2N isn't recognized as a BigInteger literal by Clojure 1.2, it seems.)
>>
>> In my copy of Clojure 1.2, format also seems to work:
>>
>> => (format "%d" (bigint 2))
>> "2"
>
> In Clojure 1.2:
>
> (type (bigint 2)) => java.math.BigInteger
>
> In Clojure 1.3:
>
> (type (bigint 2)) => clojure.lang.BigInt
> (type 2N) => clojure.lang.BigInt

What the devil? Why was this done? Seems like wheel reinvention to me.

And format should account for it. A simple change will do it:

(defn format
"Formats a string using java.lang.String.format, see
java.util.Formatter for format
string syntax"
{:added "1.0"
:static true}
^String [fmt & args]
(String/format fmt (to-array (map fixup args))))

where

(defn fixup [o]
(cond
(instance? clojure.lang.BigInt o) (.toBigInteger o)
...
:else o))

Of course, this suggests a generalization of condp:

(defn replace [rmap coll]
(let [s (map #(if-let [[_ v] (find rmap %)] v %) coll)]
(if (seq? coll)
s
(into (empty coll) s))))

(defmacro condx [symb expr & clauses]
(let [default (if (odd? (count clauses)) [:else (last clauses)] [])]
`(cond
~@(mapcat
(fn [[v x]]
[(replace {symb v} expr) x])
(partition 2 clauses))
~@default)))

(defn fixup [o]
(condx class (instance? class o)
clojure.lang.BigInt (.toBigInteger o)
...
o))

Of course, full flexibility might warrant a multimethod for fixup so
that format can be extended to any type. Format tends to occur in I/O
bound code so the runtime overhead of multimethods is probably
acceptable -- indeed, the pprint contrib library already employs them
in a very similar role.

Chas Emerick

unread,
Jul 27, 2011, 9:55:31 PM7/27/11
to clo...@googlegroups.com

On Jul 27, 2011, at 8:56 PM, Ken Wesson wrote:

>> In Clojure 1.2:
>>
>> (type (bigint 2)) => java.math.BigInteger
>>
>> In Clojure 1.3:
>>
>> (type (bigint 2)) => clojure.lang.BigInt
>> (type 2N) => clojure.lang.BigInt
>
> What the devil? Why was this done? Seems like wheel reinvention to me.

See http://dev.clojure.org/display/doc/Enhanced+Primitive+Support:

* BigInt provides hashcodes consistent with Long through the range of long
* BigInts will enable optimizations when [operations fit] in long (not yet implemented)

- Chas

Tom Faulhaber

unread,
Jul 28, 2011, 2:48:19 AM7/28/11
to Clojure
FWIW, clojure.pprint.cl-format handles this fine in 1.3:

(cl-format nil "~d" 2N)
=> "2"

On Jul 27, 11:45 am, Andrea Tortorella <elian...@gmail.com> wrote:

Sean Corfield

unread,
Jul 28, 2011, 3:05:27 AM7/28/11
to clo...@googlegroups.com
On Wed, Jul 27, 2011 at 5:56 PM, Ken Wesson <kwes...@gmail.com> wrote:
>> In Clojure 1.2:
>>
>> (type (bigint 2)) => java.math.BigInteger
>>
>> In Clojure 1.3:
>>
>> (type (bigint 2)) => clojure.lang.BigInt
>> (type 2N) => clojure.lang.BigInt
>
> What the devil? Why was this done? Seems like wheel reinvention to me.

Chas has already pointed you at the rationale / discussion but I'm a
bit surprised you reacted as if this was news - the numeric changes in
1.3 have been discussed at great length in a number of threads here
dating back over a year (and you, yourself, were active in at least
one such discussion back in December 2010).

> And format should account for it.

I can see arguments on both sides. format is clearly documented to be
a thin wrapper around java.lang.String.format so by that measure we
shouldn't expect it to handle Clojure's BigInt. OTOH, it's reasonable
to expect BigInt to behave "just like" any other regular numeric type
in Clojure and therefore Clojure's own format function should treat
BigInt as valid for %d.

Might be worth opening a JIRA ticket for enhancing format, yes?

Sean Corfield

unread,
Jul 28, 2011, 3:47:33 AM7/28/11
to clo...@googlegroups.com
On Wed, Jul 27, 2011 at 11:48 PM, Tom Faulhaber <tomfau...@gmail.com> wrote:
> FWIW, clojure.pprint.cl-format handles this fine in 1.3:
>
> (cl-format nil "~d" 2N)
> => "2"

Wow, I just spent the last 30 minutes reading Common Lisp the
Language, 2nd Ed, chapter 22 which describes how powerful and
mind-bending that is...

Andrea Tortorella

unread,
Jul 28, 2011, 7:52:53 AM7/28/11
to Clojure
Thanks for your replies,

+1 for enhancing format

Maybe it could handle also rationals, converting them to doubles, but
it could be to much.

On Jul 28, 9:47 am, Sean Corfield <seancorfi...@gmail.com> wrote:
> On Wed, Jul 27, 2011 at 11:48 PM, Tom Faulhaber <tomfaulha...@gmail.com> wrote:
> > FWIW, clojure.pprint.cl-format handles this fine in 1.3:
>
> > (cl-format nil "~d" 2N)
> > => "2"
>
> Wow, I just spent the last 30 minutes reading Common Lisp the
> Language, 2nd Ed, chapter 22 which describes how powerful and
> mind-bending that is...
> --
> Sean A Corfield -- (904) 302-SEAN
> An Architect's View --http://corfield.org/
> World Singles, LLC. --http://worldsingles.com/
> Railo Technologies, Inc. --http://www.getrailo.com/

Chas Emerick

unread,
Jul 28, 2011, 8:48:45 AM7/28/11
to clo...@googlegroups.com
Tweaking `format` so that it accounts for all sorts of edge cases almost certainly isn't going to happen, and would be a horrible kludge in any case.

To extend Tom's point, if you really want a format that knows about all of Clojure's scalars and data structures, cl-format is what you want, and it comes with the language. If you don't want to learn about cl-format, then a local wrapper around `format` that does whatever you like re: coercions to standard Java numeric types would be ~3 lines.

- Chas

Andrea Tortorella

unread,
Jul 28, 2011, 9:20:30 AM7/28/11
to Clojure
As I said tweaking `format` to work on rationals could be too much,
and i can restate that as "it is too much".

Nevertheless, extending it to work on bigint doesn't seem to me really
an edge case, given that i could get a bigint out of any function that
uses autopromotion, so:

(printf "%d" (autopromoting-factorial N))

would work only for small enough inputs. That's a bit surprising, so i
keep my +1 while I learn cl-format.

Then, if cl-format, is the "true" formatting function for clojure, why
isn't it in core?

Stuart Halloway

unread,
Jul 28, 2011, 9:24:25 AM7/28/11
to clo...@googlegroups.com
What the devil? Why was this done? Seems like wheel reinvention to me.

Understanding the motivation for such a decision requires taking the time to understand the limitations of what Java provides. Java provides no wheel here -- BigInteger's hash is broken. 

The draft docs are here: http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics. These should be made better and placed in a more prominent place before 1.3 goes final.

Stu

Stuart Halloway
Clojure/core
http://clojure.com

Perry James

unread,
Jul 28, 2011, 10:27:10 AM7/28/11
to clo...@googlegroups.com
Hi,
   Is there any way to get to those docs?  First I had to crate a user account, then I was told that
You cannot view this page
Page level restrictions have been applied that limit access to this page.
   Thanks,
   Perry

==
"This must be Thursday. I never could get the hang of Thursdays" -- Arthur Dent


On Thu, Jul 28, 2011 at 9:24 AM, Stuart Halloway <stuart....@gmail.com> wrote:
...

The draft docs are here: http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics. These should be made better and placed in a more prominent place before 1.3 goes final.
...

Ken Wesson

unread,
Jul 28, 2011, 4:53:54 PM7/28/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 3:05 AM, Sean Corfield <seanco...@gmail.com> wrote:
> On Wed, Jul 27, 2011 at 5:56 PM, Ken Wesson <kwes...@gmail.com> wrote:
>>> In Clojure 1.2:
>>>
>>> (type (bigint 2)) => java.math.BigInteger
>>>
>>> In Clojure 1.3:
>>>
>>> (type (bigint 2)) => clojure.lang.BigInt
>>> (type 2N) => clojure.lang.BigInt
>>
>> What the devil? Why was this done? Seems like wheel reinvention to me.
>
> Chas has already pointed you at the rationale / discussion but I'm a
> bit surprised you reacted as if this was news - the numeric changes in
> 1.3 have been discussed at great length in a number of threads here
> dating back over a year (and you, yourself, were active in at least
> one such discussion back in December 2010).

Discussions about primitive arithmetic, not BigInteger arithmetic.

>> And format should account for it.
>
> I can see arguments on both sides. format is clearly documented to be
> a thin wrapper around java.lang.String.format so by that measure we
> shouldn't expect it to handle Clojure's BigInt. OTOH, it's reasonable
> to expect BigInt to behave "just like" any other regular numeric type
> in Clojure and therefore Clojure's own format function should treat
> BigInt as valid for %d.

That'd be my position, yes. Also, backward compatibility: format works
in 1.2 with (* 400000000000 400000000000 400000000000) so it should
work in 1.3 with (* 400000000000 400000000000 400000000000).

> Might be worth opening a JIRA ticket for enhancing format, yes?

You go ahead. I don't have an account there.

Sean Corfield

unread,
Jul 28, 2011, 7:28:27 PM7/28/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 1:53 PM, Ken Wesson <kwes...@gmail.com> wrote:
>> Chas has already pointed you at the rationale / discussion but I'm a
> Discussions about primitive arithmetic, not BigInteger arithmetic.

I take it you didn't actually bother to read the page he linked to?
Let me quote the relevant part for you since I know how averse you are
to spending any time reading more than the first few lines of any
material that people link to:

* new clojure.lang.BigInt class
* BigInts do not auto-reduce, and are contagious (akin to doubles)
* BigInts will enable optimizations when fits in long
* optimzations not yet in place
* unlike BigInteger, BigInt has consistent hashcodes with Long,
through range of long

> That'd be my position, yes. Also, backward compatibility: format works
> in 1.2 with (* 400000000000 400000000000 400000000000) so it should
> work in 1.3 with (* 400000000000 400000000000 400000000000).

Kinda hard since that expression is not valid in 1.3 anyway:

ArithmeticException integer overflow
clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374)

So that code breaks explicitly in 1.3 and in many ways (format) is
then the least of your worries...

>> Might be worth opening a JIRA ticket for enhancing format, yes?
> You go ahead. I don't have an account there.

Ah, that's right... the contributor process is too much work for
you... Maybe one of the kind souls who've taken the time to go thru
that process might feel inclined to open such a ticket for you? If
they agree with your position, of course.

Sean Corfield

unread,
Jul 28, 2011, 7:30:47 PM7/28/11
to clo...@googlegroups.com
I think one of the authors / core members needs to change the
permissions. I have edit access on the parent page
http://dev.clojure.org/display/doc/Enhanced+Primitive+Support and the
sibling page http://dev.clojure.org/display/doc/Bit+Operations but,
like you, don't have view access to the Numerics page.

Sean

Ken Wesson

unread,
Jul 28, 2011, 7:46:27 PM7/28/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 7:28 PM, Sean Corfield <seanco...@gmail.com> wrote:
> On Thu, Jul 28, 2011 at 1:53 PM, Ken Wesson <kwes...@gmail.com> wrote:
>>> Chas has already pointed you at the rationale / discussion but I'm a
>> Discussions about primitive arithmetic, not BigInteger arithmetic.
>
> I take it you didn't actually bother to read the page he linked to?

You take it wrong. I remember that discussion, evidently better than you do.

> I know how averse you are to spending any time reading more than
> the first few lines of any material

Wrong again. Are you quite done littering the list with pointless ad
hominem remarks?

>> That'd be my position, yes. Also, backward compatibility: format works
>> in 1.2 with (* 400000000000 400000000000 400000000000) so it should
>> work in 1.3 with (* 400000000000 400000000000 400000000000).
>
> Kinda hard since that expression is not valid in 1.3 anyway:
>
> ArithmeticException integer overflow
> clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374)

Bah. It really should work, but we've had that discussion before.
Others in this thread have also pointed out that there is a backward
compatibility problem if format used to work with bignums and then
stops doing so, so you're outvoted.

> ... too much work for you...

This is not an appropriate venue for you to vent bile about other
fellow users. If you have some kind of personal issue with me, please
take it up by private email or simply keep it to yourself. Thank you.

Ken Wesson

unread,
Jul 28, 2011, 7:48:34 PM7/28/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 7:30 PM, Sean Corfield <seanco...@gmail.com> wrote:
> I think one of the authors / core members needs to change the
> permissions. I have edit access on the parent page
> http://dev.clojure.org/display/doc/Enhanced+Primitive+Support and the
> sibling page http://dev.clojure.org/display/doc/Bit+Operations but,
> like you, don't have view access to the Numerics page.

> On Thu, Jul 28, 2011 at 7:27 AM, Perry James <perry...@computer.org> wrote:

>> You cannot view this page

I can't think of a single good reason why any of these pages should
not be world-readable. We're an open source project. It's not as if we
have trade secrets or something.

I have no problem, of course, with restricting *edit* privileges to
those with accounts that have proven to be responsible adults.

pmbauer

unread,
Jul 28, 2011, 9:03:45 PM7/28/11
to clo...@googlegroups.com
That wasn't called for.

Given Stu linked to the page (and is linked in the 1.3 release notes), it's reasonable to assume the permission error is merely a mistake and not some nefarious plot to withhold information from the Clojure community.

Ken Wesson

unread,
Jul 28, 2011, 9:11:06 PM7/28/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 9:03 PM, pmbauer <paul.mich...@gmail.com> wrote:
> That wasn't called for.

??

> Given Stu linked to the page (and is linked in the 1.3 release notes), it's
> reasonable to assume the permission error is merely a mistake and not some
> nefarious plot to withhold information from the Clojure community.

Did I say it was a "nefarious plot"?

The very fact that the web site is set up with a "You cannot view this
page" message dependent on your cookie-login and separate from the
low-level HTTP 403 error means that it's been set up to make some
information members-only. Whether that *particular* page on numerics
was intended to be members-only or not, the fact remains that
apparently some information there *is* intended to be members-only. It
is *that* that I am questioning.

Regardless, your "that wasn't called for" doesn't make much sense
since I merely stated a fact ("I can't think of ..."), another fact
("We're an open source project"), another fact (we don't have trade
secrets), and one more fact ("I have no problem, of course, with
..."). If someone is taking mere *facts* (two of which are about *me*)
personally then I'd say there may be a problem but the problem isn't
mine. :)

Stuart Halloway

unread,
Jul 29, 2011, 1:03:17 AM7/29/11
to clo...@googlegroups.com
I can't think of a single good reason why any of these pages should
not be world-readable. We're an open source project. It's not as if we
have trade secrets or something.

I have no problem, of course, with restricting *edit* privileges to
those with accounts that have proven to be responsible adults.


Thanks,

Meikel Brandmeyer (kotarak)

unread,
Jul 29, 2011, 1:52:39 AM7/29/11
to clo...@googlegroups.com
Hi,

 
Am Freitag, 29. Juli 2011 01:28:27 UTC+2 schrieb Sean Corfield:
Kinda hard since that expression is not valid in 1.3 anyway:

ArithmeticException integer overflow
clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374)

So that code breaks explicitly in 1.3 and in many ways (format) is
then the least of your worries...

I think this is one of the misunderstandings or points of disagreements (or whatever you want to name it) in this whole discussion: this code is *not* broken. And the expression is also perfectly valid. The function call just throws an exception. This can be handled. Or you use *' which won't overflow but give a BigInt in that case.

So you are in full control of what your program does. Nothing is broken here. One just hast to pay the price for special handling. The JVM doesn't allow fast and non-overflow in the same operation. Clojure chose to go the fast route with primitive math. And consequently non-overflow became special handling. So where is the issue?

Sincerely
Meikel

Ken Wesson

unread,
Jul 29, 2011, 2:24:54 AM7/29/11
to clo...@googlegroups.com

Er, "fast" would be for primitive integer arithmetic to wrap rather
than throw an exception or auto-promote. Both of the latter behaviors
require every math op to be accompanied by a test of some sort and a
branch (to either the exception-throwing code or the BigInt
constructing code).

Meikel Brandmeyer (kotarak)

unread,
Jul 29, 2011, 2:36:51 AM7/29/11
to clo...@googlegroups.com
Hi,


Am Freitag, 29. Juli 2011 08:24:54 UTC+2 schrieb Ken Wesson:

Er, "fast" would be for primitive integer arithmetic to wrap rather
than throw an exception or auto-promote. Both of the latter behaviors
require every math op to be accompanied by a test of some sort and a
branch (to either the exception-throwing code or the BigInt
constructing code).

The exception throwing check is much faster than boxing each and every number (Java can only do primitive return or Object, so you have to box everything on return.)

If you want "really fast", there is the unchecked-math flag or the unchecked-* operations.

You have "really fast, unsafe" -> "fast, safe" -> "slow, safe". Clojure chose the middle path. No one prevents you from deviating to the left or the right via unchecked-* or *'.

Sincerely
Meikel

 

Ken Wesson

unread,
Jul 29, 2011, 2:46:01 AM7/29/11
to clo...@googlegroups.com

Yes. I felt the other post may have been unintentionally misleading,
by implying that checking and maybe throwing an exception was the
fastest path.

Sean Corfield

unread,
Jul 29, 2011, 3:49:36 AM7/29/11
to clo...@googlegroups.com
On Thu, Jul 28, 2011 at 10:52 PM, Meikel Brandmeyer (kotarak)
<m...@kotka.de> wrote:
> I think this is one of the misunderstandings or points of disagreements (or
> whatever you want to name it) in this whole discussion: this code is *not*
> broken.

Code that runs on 1.2 but throws an exception on 1.3 is "broken" on
1.3 by definition. The fact that it can be easily "unbroken" is good
to know but it still means working code can stop working if you move
from Clojure 1.2 to 1.3. That's what I mean by 1.3 "breaks" code.

Some people are more upset by this than others (I'm not upset by it at
all, just for the record - I think the decision is the right one for
the future of Clojure).

Besides, after what I (and many others) experienced in the Scala 2.7 /
2.8 upgrade, the incompatibilities between Clojure 1.2 and 1.3 are
positively minor by comparison :)

Reply all
Reply to author
Forward
0 new messages