pragma Fast_Math

──というのがいつのまにか追加されてた。
http://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Fast_005fMath.html
規格上保たないといけない精度とか無視して突っ切るためのpragmaらしい。
紛らわしいですがきっと-ffast-mathとはなんの関係も無いです。そもそもCフロントエンドがintrinsic扱いしてるかどうかなんてのは他のフロントエンドには関係……いや展開してるとこがCフロントエンドの中にあるわけじゃないですし本来関係あるはずなんですが少なくともGNATフロントエンドだと関係ないです。
大体がAdaのべき乗って**ですし、ふつーに畳み込んでくれるよな、GNATフロントエンド……激しく信用できないな。

function a return integer is
begin
   return 3 ** 4;
end;

オプション無し-S

  .file  "a.adb"
  .text
.globl __ada_a
  .def  __ada_a;  .scl  2;  .type  32;  .endef
__ada_a:
LFB3:
  pushl  %ebp
LCFI0:
  movl  %esp, %ebp
LCFI1:
  movl  $81, %eax
  popl  %ebp
  ret
LFE3:
  .section  .eh_frame,"dr"
Lframe1:
  .long  LECIE1-LSCIE1
LSCIE1:
  .long  0x0
  .byte  0x1
  .def  ___gnat_eh_personality;  .scl  2;  .type  32;  .endef
  .ascii "zP\0"
  .uleb128 0x1
  .sleb128 -4
  .byte  0x8
  .uleb128 0x5
  .byte  0x0
  .long  ___gnat_eh_personality
  .byte  0xc
  .uleb128 0x4
  .uleb128 0x4
  .byte  0x88
  .uleb128 0x1
  .align 4
LECIE1:
LSFDE1:
  .long  LEFDE1-LASFDE1
LASFDE1:
  .long  LASFDE1-Lframe1
  .long  LFB3
  .long  LFE3-LFB3
  .uleb128 0x0
  .byte  0x4
  .long  LCFI0-LFB3
  .byte  0xe
  .uleb128 0x8
  .byte  0x85
  .uleb128 0x2
  .byte  0x4
  .long  LCFI1-LCFI0
  .byte  0xd
  .uleb128 0x5
  .align 4
LEFDE1:

されてた。良かった。
これが畳み込まれてないと、1 shl n = 1 * 2 ** n, 1 shr n = 1 / 2 ** nで代用*1している箇所がえらいことになることろでした。良かった。
別に-ffast-math付けても変わりませんでした。
そうそう、4.3から遂にMinGWでもzcxですよ。system-mingw.adsのZCX_By_DefaultをTrueにしてやれば有効になります。
閑話休題してpragma Fast_Mathです。
関係ないですが連想されるのがDelphi.NET上の{$FINITEFLOAT OFF}。倍近く計算速度に影響していました。今となっては、.NETで速度が要求される計算なんてする方が間違ってると断言できますが、当時は衝撃でした。なにしろDelphi8時点ではUndocumented(ry
ところでDelphi2007のヘルプのUpdateが出てたのであてたのです。確かにあちこちで地味に言われているように品質は少しマシになっていたのですが、折角Delphiのみの版で、Delphi for Win32とWindows SDKだけの見通しの良い状態だったのに、C++Builderと.NET SDKのヘルプが登録されてorzなんですが……2007にはreghelp.exeが付いてないため外し方がわかりませんorz
閑話休題してpragma Fast_Mathです。
要するに、a-nucoty.adsと、それにpragma Fast_Math;を書き足したのとで、複素数の乗算を比べればいいらしいです。
しかし複素数の乗算って要するにa-ngcoty.adbの"*"が使われるだけでは?

   function "*" (Left, Right : Complex) return Complex is
      X : R;
      Y : R;

   begin
      X := Left.Re * Right.Re - Left.Im * Right.Im;
      Y := Left.Re * Right.Im + Left.Im * Right.Re;

      --  If either component overflows, try to scale (skip in fast math mode)

      if not Standard'Fast_Math then
         if abs (X) > R'Last then
            X := R'(4.0) * (R'(Left.Re / 2.0)  * R'(Right.Re / 2.0)
                            - R'(Left.Im / 2.0) * R'(Right.Im / 2.0));
         end if;

         if abs (Y) > R'Last then
            Y := R'(4.0) * (R'(Left.Re / 2.0)  * R'(Right.Im / 2.0)
                            - R'(Left.Im / 2.0) * R'(Right.Re / 2.0));
         end if;
      end if;

      return (X, Y);
   end "*";

……わかりやすいですね。試すまでも無いです。
pragmaの他に属性Standard'Fast_Mathもいつのまにか追加されてて、これは単にpragma Fast_Mathの状態を返す、と……。汎用の条件コンパイル用のpragma入れたくないのだろうな、と勝手に想像した上で、それには賛成ですけれども……。こんなのAda.Numerics.Generic_Complex_Typesにデフォルト値付きのBoolean引数足したって一緒じゃないですか。あ、それだとpackage自体を仮引数宣言する時にothersが要るようになって互換性崩れるのかな。
むしろオーバーフローの判定式がabs (X) > R'Lastなのが驚きです。属性S'Machine_Overflowsってなんのためにあるのでしょうか……。あ、もしかしてS'Machine_Overflowsって例外発生中のみTrueになるのか……。全部Constraint_Errorになってしまう計算による例外の原因を分類するための属性なわけですね。使えねえ。
つかAdaCoreがresyncと称してgcc-patchesにもアナウンス無しで一度に大量のcommitを行うのは、gcc的にはどう思われているのか知りたいこの頃。--input-charsetや--exec-charsetを無視して独自にスイッチ設けるフロントエンドとかさ、他のフロントエンドは許していていいの?曲がりなりにも標準配布に含まれてる言語なのに?……なんて激しく思うのですが、きっとgccの中の人ですらAdaフロントエンドの惨状は誰も把握どころか単に見てないに違いないという容易な想像が。4.2になれど4.3になれどGNATはchanges.htmlすら書かれず、じっとFortranを見る。

*1:説明するまでも無いですがshlとshrPascal演算子です。Adaにはシフト演算子は無いのです。